Repository navigation
Draw 「Pipeline by Stage」 as a ranked bar, not a funnel - #57
Merged
Merged
Conversation
A funnel encodes monotonic decline. The seeded book does not decline —
draft 10 / submitted 6 / in_review 12 / in_approval 8 / approved 4 /
signing 6 / active 60 — so the mark rendered as a bowtie and, worse,
asserted that contracts fall out at each stage. They do not: this is a
snapshot of where a live book sits, not a conversion sequence.
A bar over the stage dimension cannot bowtie at any distribution, which
matters because the corpus is dealt by a fixture and re-dealt by other
cards.
Order comes from the measure, not from a label. `options.stageOrder` is
read only by the funnel branch of the console renderer (measured in its
code and in the browser), and in a zh-CN console it was already being
ignored: the labels fall back to English while the client's order map
holds the bundle's Chinese ones, so the chart fell back to alphabetical
order. `sortBy: 'contract_count'` lowers to `order: { contract_count:
'desc' }` on the dataset query and cannot be reordered by any label in
any locale.
The widget's description changes with the mark, so both bundles change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
zhuangjianguo
marked this pull request as ready for review
September 10, 2026 07:55
This was referenced Sep 10, 2026
zhuangjianguo
pushed a commit
that referenced
this pull request
Sep 10, 2026
§09 still specified 各阶段合同数漏斗 — a funnel over every lifecycle stage. Two merged cards moved the app off that in two independent ways, and the design authority never caught up: - #48 / PR #57 replaced the mark with `type: 'horizontal-bar'` ordered by the measure, because a funnel asserts monotonic decline over a book that does not decline (§10: draft 10 · submitted 6 · in_review 12 · in_approval 8 · approved 4 · signing 6 · active 60 — a bowtie). - #59 / PR #62 narrowed the filter to the six in-flight stages, on the criterion the widget's comment states: whether legal still has work to do on the stage. `active` and the four terminal outcomes are all out. The bullet now names the mark and the coverage; a new paragraph carries the reasoning and states that no authored stage order is promised — `options.stageOrder` is read only by console 17.4.0's funnel branch and is dropped silently outside `en` (objectstack-ai/objectstack#17344). Promising an ordering this platform version cannot deliver is the same defect class PR #40 just removed from this section. Documentation only: no app file changes, and no gate reads `DESIGN.md`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
zhuangjianguo
pushed a commit
that referenced
this pull request
Sep 10, 2026
…ages (#63) 09 still specified the Legal Workbench stage widget as 各阶段合同数漏斗 — a funnel over every lifecycle stage. The app stopped shipping a funnel in #48 (PR #57 made it a horizontal-bar ordered by the measure) and narrowed what it plots in #59 (PR #62 dropped `active`, leaving the six in-flight stages), and the design authority never caught up. The bullet now names the mark and the coverage, and a new paragraph carries the reasoning: a funnel asserts monotonic decline and this book does not decline (10's spread — draft 10 · submitted 6 · in_review 12 · in_approval 8 · approved 4 · signing 6 · active 60), so the mark was asserting something the numbers do not say. The admission criterion is whether legal still has work to do on the stage, which is why `active` and the four terminal outcomes are all out; the size of the in-force book belongs to the 管理层 board. It also records the non-promise, which is the half a list bullet cannot carry: 09 must NOT describe an authorable stage order. options.stageOrder was removed deliberately in PR #57 — console 17.4.0 reads it only in the funnel branch and drops it silently outside `en` (objectstack-ai/objectstack#17344). Recording the negative is what stops the next reader re-adding it, the same way PR #40 recorded rather than silently deleted the 各段时长 promises from this section. Sits alongside PR #40's 09 rewrite: the 各段时长 paragraph and every other row are byte-identical, and 01-04 are unchanged. No app file is touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
zhuangjianguo
pushed a commit
that referenced
this pull request
Sep 10, 2026
The legal dashboard's header carried a copy of §09's six-brick list. That copy drifted three times in two days — two items rewritten by PR #40 (1127e52), the third by PR #70 (30fd863) — and by the end all three described bricks this file does not implement. Replace the copy with a pointer to §09, which cannot drift. The cycle-time dataset's stage-count paragraph contrasted its counts with "the status funnel on the legal workbench", a funnel that has not existed since PR #57 made that widget a bar chart. Keep the measurement and its reason (signed_at 72 exceeds submitted_at 60; backfilled in-force book, no intake) and drop the claim that a funnel exists to contrast with. Comments only: no metadata, no behaviour, no DESIGN.md change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
zhuangjianguo
pushed a commit
that referenced
this pull request
Sep 10, 2026
Two comments quoted a 09 that has since moved — the mirror of the cards that fixed documents describing code wrongly. legal.dashboard.ts's header carried a copy of 09's six-item brick list, and the copy lost the race three times in two days: two items were rewritten by PR #40 (1127e52) and the third by PR #70 (30fd863), leaving all three describing bricks this file does not implement — the board ships review_ageing titled "In Review Over 30 Days", approval_throughput titled "Approved This Month", and a horizontal-bar. Two of them named metrics 09 now explicitly declines to deliver. The quote is removed rather than updated: updating buys correctness until the next 09 edit and leaves the same trap armed, while a pointer cannot drift. The header now says the list is deliberately absent, so the next reader does not paste it back, and cites commit shas — immutable — instead of prose. cycle-time.dataset.ts contrasted its five coverage counts with "the status funnel on the legal workbench", which has been a horizontal-bar since PR #57. The contrast is inverted rather than deleted: there is no funnel there either, and the same non-monotone book is the reason. The measurement stays — signed_at (72) exceeding submitted_at (60), and the backfill that explains it. Comments only: no metadata key, no widget, no dataset field, no dimension. These files are inside the gates' surface and the gates pass — but comments are invisible to tsc, which is why the drift went unnoticed for two days. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #48
Pipeline by Stageon 法务工作台 was authored as a funnel. A funnel encodes monotonic decline — each stage narrower than the last, because contracts drop out between them. This book does not decline, so the mark was asserting something the numbers do not say. It is now a ranked bar over the same dimension and the same measure.What the data actually is (measured, not assumed)
Counted out of the running database,
POST /api/v1/analytics/dataset/queryoncontract_metricswith dimensionstatusand no filter — all ten statuses, 120 contracts:The card's premise holds: the late stages outnumber the thin
draft/submittedhead, by 60 to 10. (The card says "active/signed"; there is nosignedstatus —signingis 6 andactiveis 60.) This is DESIGN.md §10's spread, pinned byassertSpreadinsrc/data/plan-contracts.ts, and it is unchanged onorigin/main@d34b225after #47/#55 landed — that card re-dealt which types carryin_review, not how many contracts sit in each status.Rendered as a funnel that shape is a bowtie. Measured band widths in Chromium, before the change: 115 · 138 · 138 · 92 · 69 · 692 · 692 px — narrow, wide, narrow, then eleven times wider at the end.
The fix, and why it is correct at any distribution
type: 'funnel'→type: 'horizontal-bar'. A bar over the stage dimension says exactly what the data says and cannot bowtie at any distribution — which is the requirement, not a preference: the corpus is dealt by a fixture and re-dealt by other cards, so a mark that merely suited today's counts would break again quietly.options.stageOrder→options: { sortBy: 'contract_count', sortOrder: 'desc' }. See below — this is the ordering half of the card, and the answer turned out to be measurable rather than assumable.descriptionchanges with the mark ("intake through activation" described an order the chart no longer has), so both translation bundles change with it. No other string moves; the title is untouched in both locales.No data was reshaped.
src/data/,README.mdandDESIGN.mdare untouched.The ordering question — answered, and it is not ours to fix
options.stageOrderis lowered by the dashboard renderer onto every chart type ascategoryOrder, and only the funnel branch reads it. Measured twice on@objectstack/console17.4.0:categoryOrderis consumed inside thechartType === 'funnel'branch and nowhere else; the bar branch passes the rows through untouched.horizontal-barwithstageOrderstill authored rendered Active · Approved · Draft · In Approval · In Review · Signing · Submitted — alphabetical.So an explicit lifecycle order is not authorable for a bar on this platform version, and keeping
stageOrderwould have been metadata that does nothing. It is removed rather than left inert, and reported upstream rather than patched.What is authorable is an order that cannot depend on a label, and that is the robustness the card was after. The zh-CN measurement before the change is the reason it matters:
The mechanism: the query rows carry the API's English labels while the client's order map carries the bundle's Chinese ones, so nothing matches and every row falls to the same sort sentinel. The label bug was silently reordering the chart, exactly as the card suspected.
sortByon the measure cannot be reordered by any label in any locale. It lowers toorder: { contract_count: 'desc' }on the dataset query (verified in the request body), and the rendered order is now byte-identical in both locales:Including the 6–6 tie between
SubmittedandSigning, which lands the same way both times.The "spills outside its card" half does not reproduce
That claim came from a screenshot in the #45 dogfood pass, and it was re-checked rather than inherited. On
main@2807b3bwith console 17.4.0, the funnel's ink sits 25px inside the card on every side at 1440 / 1280 / 1024 / 820 px viewport widths, withscrollWidth === clientWidth(no horizontal scroll) at each. The mark was wrong-shaped, not overflowing. Nothing about the fix changes; the card should just not carry a claim nobody re-checked. The bar measures the same 25px inset at all four widths.Browser verification
pnpm demoon a clean database (port 3048), signed in asadmin@objectos.ai, navigated to Dashboards → Legal Workbench, in anenand then azh-CNconsole, before and after the change; the widget was screenshotted in all four states.SigningandActive.Contractsaxis 0–60, every stage label legible.Activeband at the top — English labels, alphabetical order.en. The category labels are still English — that is the platform gap the card routes upstream, unchanged and untouched here.Console during both passes: two lines, identical before and after and therefore pre-existing — a
404on a resource fetch and[AuthProvider] Failed to load organizations: TypeError: Failed to fetch. Boot carried only the two known platform ERROR lines (objectstack#17175,#17176) and the background seed continuation (objectstack#17329).Gates
Exit codes captured before any pipe (
cmd > file 2>&1; EXIT=$?).Acceptance notes
stage_funnel. It is the key both bundles address this widget by, it is not user-visible, andtypeone line below is what states the mark; renaming it would move three files for no user-visible gain.suppressWarnings: ['chart-config-missing']is kept and is inert on this lint version — measured in both directions: removing it produces the same21 warning(s), 5 suggestion(s)on this branch and on base2807b3bwith the funnel. It is pre-existing, unaffected by this change, and left alone rather than tidied under this card.dashboard.widgets[].options.stageOrderis forwarded to every chart type ascategoryOrderand honoured only by the funnel branch, so an authored stage order is silently dropped on a bar/column widget. Noted, not filed here — it belongs in objectstack-ai/objectstack, and this card owns no issue-filing on that repo.🤖 Generated with Claude Code
https://claude.ai/code/session_01R3n3GGzobdegM4HUzah1iR
Generated by Claude Code