chore(spec): regenerate the api-surface baseline for the percent-scale exports (#4442) - #4523
Merged
Merged
Conversation
…e exports `check:api-surface` is a snapshot gate: every export reachable from a `@objectstack/spec` entry point is listed in `api-surface.json`, and the check fails when the built surface and the committed list disagree — in either direction. It is what makes an unintended public-API change impossible to land silently. The percent-scale work added four exports to `./data` — `PercentScale`, `PercentScaleFieldMeta`, `percentScaleOf` and `emptyGroupValueFor` — without refreshing the baseline, so the gate reported them as 4 unrecorded additions and failed the TypeScript Type Check job. They are additions, deliberately: `percentScaleOf` is the answer-in-one-place the service reads, and the renderer half of objectui#3136 consumes the annotation it produces. Nothing was removed or narrowed, so this is not a breaking surface change. Regenerated with `pnpm --filter @objectstack/spec gen:api-surface`; the diff is exactly those four lines. `api-surface-signatures.json` is untouched — no factory signature moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S9TVFwXGsXqR3SD5e2qAU8
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
The changeset gate requires every PR to add a `.changeset/*.md`, counted by diffing against the PR base rather than globbing the directory — in RC pre-mode `changeset version` retains consumed files, so a global count can never go red. This PR added none and the gate failed, correctly. An empty-frontmatter changeset is the sanctioned "this PR releases nothing" declaration, and that is the honest answer here. `api-surface.json` is a build-time snapshot the `check:api-surface` gate diffs against, not shipped code — regenerating it changes no runtime behavior and publishes no new API. The four exports it now records already ship under the percent-scale changeset on the base branch, so declaring a bump here would double-count that same release. `check-changeset-no-major.mjs` is satisfied for the same reason: an empty changeset declares no bumps at all, least of all a `major`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S9TVFwXGsXqR3SD5e2qAU8
os-zhuang
marked this pull request as ready for review
August 1, 2026 16:17
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 2, 2026
…report an empty filtered group as zero (objectui#3136) (objectstack-ai#4442) * fix(spec,service-analytics): carry a percentage measure's SCALE on the result column (objectui#3136) A `%` format string says how to print a number, not what scale it is on, and the two readings collide at exactly 1 — both "100%" (a 0-1 ratio) and "1%" (one percentage point). Renderers guessed from the value's magnitude and resolved it the wrong way, so an SLA rate of full compliance displayed as "1.0%". The scale was answerable from metadata all along; it just never left the server. `derived: { op: 'ratio' }` is a 0-1 fraction by definition, and a measure over a `percent` field has that field's scale. Both are now resolved in the measure-column enrichment pass, next to the ADR-0053 currency chain that already walks back to the source field for exactly this kind of display fact. - `percentScaleOf(field)` (spec/data) — the one rule: a `percent` field stores a fraction unless it declares `max > 1`, matching what the edit widget writes. Non-percent fields get no opinion. - `AnalyticsResult.fields[].percentScale` — 'fraction' | 'whole', absent when the column is not a percentage. `currency` (emitted since ADR-0053 through a cast) is declared on the same interface. - `measureCurrency` → `sourceFieldMeta`, now returning `max`. The old name had outgrown itself: date bucketing already read `type` through it, and the percent chain is its third consumer. - Showcase: a `paid_rate` ratio measure + KPI/table widgets on Revenue Pulse. Grouping by status pins the Paid bucket at exactly 1 — the repro value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(spec,service-analytics): an empty filtered group is a measured zero, not missing data (objectui#3136) A measure-scoped filter can exclude every row of a group the grid still lists, and the database reports that by omitting the group from the supplementary result — after the merge, indistinguishable from "never measured". For a COUNT or a SUM it IS measured: the answer is 0. So "0 of 12 paid" rendered as a blank cell and every ratio built on it went null — a compliance dashboard silently dropping the row it exists to show. On the showcase's Paid-Rate table the Sent bucket read "—/—" where the truth is "0 / 0.0%". - `emptyGroupValueFor(aggregate)` (spec/data/aggregation-policy) states which aggregates have an identity over the empty set. avg/min/max keep their null: there is nothing to average, and a zero there would invent a measurement. - `queryDataset` fills it in after ALL supplementary merges, not inside the loop — a later measure's merge can append rows for dimension keys no earlier query saw, and those rows need the same fill. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(spec): regenerate the api-surface baseline for the percent-scale exports (objectstack-ai#4523) Regenerates `packages/spec/api-surface.json` so the committed baseline records the four `./data` exports the percent-scale chain adds — `PercentScale`, `PercentScaleFieldMeta`, `percentScaleOf` and `emptyGroupValueFor` — plus an empty-frontmatter changeset declaring that this releases nothing. The api-surface gate is a snapshot check: it fails whenever the built public surface and the committed list disagree, in either direction, which is what makes an unintended public-API change impossible to land silently. objectstack-ai#4442 added the exports without refreshing the baseline, so `check:api-surface` reported 4 unrecorded additions and failed the TypeScript Type Check job. Nothing was removed or narrowed — 0 breaking. The changeset is empty on purpose: `api-surface.json` is a build-time snapshot, not shipped code, and the exports it records already ship under `dataset-percent-scale-chain.md` on this branch. A bump here would double-count that release. * fix(showcase): translate the two percent-scale widget titles at birth `check-i18n-coverage` failed the TypeScript Type Check job: showcase's untranslated declared strings grew 452 → 454. The two new strings are the Revenue Pulse widgets this branch adds — `kpi_paid_rate` ("Paid Rate") and `table_rate_by_status` ("Paid Rate by Status") — declared with English titles and no zh-CN, a locale the example claims to support. This defect was always here; it was simply unreachable. The job used to die one step earlier on `check:api-surface`, so it never got as far as the i18n gate. Fixing the baseline uncovered it. Translated rather than baselined. The ratchet tolerates the debt that predates it and refuses growth, so the honest remedy for a string this branch introduces is to translate it — and the convention is already written down two lines above, where `showcase_chart_gallery`'s newest widget is translated at birth while its older siblings stay frozen. Revenue Pulse's other eight widget titles predate the ratchet the same way and are left alone; cleaning those up is not this branch's business. Terminology follows the existing zh-CN invoice bundle (发票 / 状态 / 付款). Count returns to exactly 452, so `scripts/i18n-coverage-baseline.json` needs no edit — no ratchet-down, no baseline churn. Verified: `check-i18n-coverage` OK (12 configs, none new); `check-i18n-bundles` all in sync; showcase `tsc --noEmit` clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S9TVFwXGsXqR3SD5e2qAU8 --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: os-zhuang <jack@objectstack.ai>
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 the failing TypeScript Type Check job on #4442. Targets that PR's branch, so merging this turns #4442 green in place.
What was red
Every job on #4442 passed except
TypeScript Type Check, and that job did not fail on a type error — all 117turbo typechecktasks, the four examples, anddownstream-contractwere clean. It failed on its last step,pnpm --filter @objectstack/spec check:api-surface:api-surface.jsonis a committed snapshot of every export reachable from a@objectstack/specentry point, and the gate fails whenever the built surface and that list disagree — in either direction. That is the point: it makes an unintended public-API change impossible to land silently. #4442 added four exports to./dataand never refreshed the baseline, so the gate did exactly its job.What lands
packages/spec/api-surface.json— regenerated. The additions are deliberate:percentScaleOfis the single place the percent-scale question is answered,emptyGroupValueForstates which aggregates have an identity over the empty set, and the Console half (objectui#3140) consumes thepercentScaleannotation they produce. The report itself says 0 breaking — nothing removed, nothing narrowed, so no downstream consumer loses anything. Four lines, matching the four names CI listed:Generated by
pnpm --filter @objectstack/spec gen:api-surface, not hand-edited.api-surface-signatures.jsoncame back byte-identical — no factory signature moved — so it is not in the diff.An empty-frontmatter changeset. The first push turned
Check Changesetred: the gate requires every PR to add a.changeset/*.md, counted by diffing against the PR base rather than globbing the directory (in RC pre-modechangeset versionretains consumed files, so a global count can never go red). An empty changeset is the sanctioned "this PR releases nothing" declaration, and that is the honest answer here —api-surface.jsonis a build-time snapshot the gate diffs against, not shipped code, and the four exports it records already ship underdataset-percent-scale-chain.mdon the base branch. Declaring a bump would double-count that same release.check-changeset-no-major.mjspasses for the same reason: no bumps declared at all.Verification
spec check:api-surfacepublic API surface + factory signatures unchanged ✓(was exit 1)spec typecheck(tsc --noEmit)specpercent-scale + aggregation-policy testsservice-analyticsquery-dataset + granularity-postprocess testscheck-changeset-no-major.mjsmain(fd3013a)Note on CI scope:
ci.ymltriggers only on PRs whose base ismain, so theTypeScript Type Checkjob carrying the api-surface gate does not run on this PR — it re-runs on #4442 once this merges. The gate was verified locally against a realspecbuild.mainhas moved 3 commits since #4442 was opened; the branch still merges without conflict, so no rebase is needed.🤖 Generated with Claude Code
https://claude.ai/code/session_01S9TVFwXGsXqR3SD5e2qAU8