Skip to content

feat(i18n,plugin-calendar,plugin-timeline): the calendar and the timeline start the week on the locale's first day (objectui#11675) - #11710

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11675-week-start-from-locale
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11675-week-start-from-locale

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11675
Clause-②: yes

The calendar and the timeline now start the week on the first day of the week of the user's locale, as ruling B on the card asks (the maintainer's 「同意」, recorded in comment 6010771237): under en-US both start on Sunday, under en-GB or zh-CN both start on Monday, from the locale's week info. Before this, the calendar always started on Sunday and the timeline's "This week" always on Monday. No setting is added: the first day comes from the tag each component already formats its dates with.

Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju (dispatched os-dev, seat domain:ui#2, claim comment 6012945073).

Premise check, on origin/main at daa7caf

  • Calendar: the Sunday start was in CalendarView.tsx: getMonthDays padded the first row by firstDay.getDay(), getWeekStart stepped back by getDay(), the month view's weekday heads ran from a reference Sunday, and a span wrapping into a new row was titled on getDay() === 0. getWeekStart also fed the week view's columns and the header's week range.
  • Timeline: ObjectTimeline.tsx's bucket bounds used startOfWeek = today - ((now.getDay() + 6) % 7) * day, a Monday start.
  • No shared helper existed: git grep for weekInfo, getWeekInfo, firstDayOfWeek and weekStartsOn under packages/*/src answered nothing, with the control word firstDay (a local in getMonthDays) answering.
  • The premise holds. The only other week-related site is the gantt variant's week axis, which counts plan weeks from the axis's first day and does not snap to a weekday by design (its own docblock in renderer.tsx says so). It is left as it is (see Acceptance notes).

What changed

@object-ui/i18n exports firstDayOfWeek(locale) and WeekdayIndex (src/utils/first-day-of-week.ts). It returns 0 to 6 as Date.prototype.getDay numbers weekdays, which is also react-day-picker's weekStartsOn. Both plugins already import this package, and it hosts useDisplayLocale, the locale both format with.

  • Engine first: Intl.Locale's getWeekInfo(), else the weekInfo accessor (ISO 1 to 7, 7 is Sunday), so a -u-fw- keyword is honoured.
  • Measured engine support: Node 22.22 (V8 12.4, the test and CI runtime) has the accessor only. Chromium 141 (the preinstalled Playwright build) has both. Firefox was not measured: no Firefox build is in this container.
  • Where the engine has neither: CLDR's weekData first-day table, keyed by the tag's region or the one maximize() gives it (en reads US, zh reads CN). An unlisted region reads Monday, CLDR's world default 001. This is the deterministic fallback, and the tests cover it.
  • The table transcribes one CLDR release, declared in the source as WEEK_DATA_CLDR (48, the release CI's floating 22.x Node ships), and was generated from Node's ICU. Engines carry their own week data, and it differs: CLDR 48 moved Iceland to a Sunday start, and Chromium 141 starts AE on Saturday where Node's CLDR 46, 47 and 48 all start it on Monday. That is why the engine's answer wins wherever there is one. A parity case re-derives the table against the running engine, region by region (see Patch round 1).
  • A malformed tag (en_US) throws Intl.Locale's RangeError, as toLocaleDateString does for the same tag in the same render. No day is guessed.

@object-ui/plugin-calendar. CalendarView computes weekStart = firstDayOfWeek(effectiveLocale) once (the authored locale prop, else the display locale) and passes it to every site:

  • the month grid's leading days and its weekday heads;
  • the title day of a span that wraps into a new row;
  • the week view's columns and the header's week range;
  • the date popover, through weekStartsOn.

The popover used to take date-fns's own weekStartsOn. That is not CLDR's for every tag: date-fns has no es-MX, so the tag reads es, a Monday start, where CLDR starts Mexico's week on Sunday. It also read enUS, a Sunday start, until the locale loaded. The new memo dependencies include weekStart.

@object-ui/plugin-timeline. ObjectTimeline reads useDisplayLocale(), the same hook its renderer formats with, and starts "This week" on firstDayOfWeek(displayLocale). The bounds block was rewritten as local midnights stepped on the local calendar (daysFromToday(n)), which includes tomorrow, in place of today + n * 86400000. Across a DST change the old arithmetic missed "Tomorrow" and put the first day of next week into "This week". Measured in the driven zone pin (America/Los_Angeles, Sunday 8 March 2026): the 9th now reads "Tomorrow" and the 15th "Next week". This is a published defect inside the block this change rewrites, so it is corrected here rather than filed (see Acceptance notes).

Built-declaration reading (Clause-②): packages/i18n/dist/utils/first-day-of-week.d.ts declares export type WeekdayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6; and export declare function firstDayOfWeek(locale: string): WeekdayIndex;. dist/index.d.ts re-exports both. So the helper widens the package's public surface, and Clause-②: yes stands.

Docs: the i18n README (a "The first day of the week" subsection), the calendar and timeline READMEs, and content/docs/plugins/plugin-calendar.mdx / plugin-timeline.mdx. Changeset: .changeset/11675-week-start-from-locale.md, minor for the three packages.

Tests

  • New packages/i18n/src/__tests__/firstDayOfWeek-11675.test.ts (14 cases):
    • the card's pins on both paths, plus a Saturday and a Friday region;
    • -u-fw- (read by the engine only), and getWeekInfo() read before weekInfo;
    • the RangeError;
    • a parity case: for every region the runtime names, the regions where the table and the engine disagree must equal exactly the ones a measured ledger (RELEASE_DELTAS) records for the runtime's CLDR release, which is none on the table's own release. A rig case and a ledger-honesty case sit beside it;
    • a check that both prototype spellings are restored, because the unit project runs isolate: false.
  • New packages/plugin-calendar/src/__tests__/CalendarView.weekStartFromLocale-11675.test.tsx (10 cases):
    • the month grid and its heads under en-US, en-GB, zh-CN and ar-EG, and under an authored locale;
    • the week view's columns and header range;
    • the wrapped span's title day;
    • the popover under en-GB and under es-MX, the case where date-fns and CLDR disagree.
  • New packages/plugin-timeline/src/__tests__/ObjectTimeline.weekBucketsFromLocale-11675.test.tsx (4 cases): the buckets under en-US, en-GB, zh-CN and ar-EG.
  • Added to the driven zone pin ObjectTimeline.dateOnlyZone-10866.test.tsx, which scripts/__tests__/date-only-zone-pins-10183.test.ts already drives: a DST block with a rig case, a fixture-validity case (8 March is 23 hours long there) and the buckets case.
  • Re-judged, not weakened: the weekday-head helpers in CalendarView.gridcellLocale-10144.test.tsx and CalendarView.displayLocale-10442.test.tsx modelled a Sunday-first grid, so they went red under de / de-CH / fr-FR. Their purpose is the heads' language, not their order. They now take the first day as an explicit literal: Monday for de / de-CH / fr-FR, Sunday for en-US. Each still asserts the full head row.

Ablations

Every leg ran through objectstack's scripts/ablation-replace.mjs, from the committed tree. Each leg proved the mutation landed: the anchor count went from 1 to 0 and the blob changed. Each leg proved the restore: the blob matched HEAD and git diff HEAD was empty. No build was needed: vitest aliases @object-ui/i18n to packages/i18n/src, and the plugin tests import the components by relative path.

Leg Mutation Predicted Observed
A1a timeline daysIntoWeek hard-coded Monday en-US, ar-EG red; en-GB, zh-CN green 2 failed / 2 passed, as predicted
A1b endOfWeek back to 24-hour steps (zone driver) the DST case red the driver's timeline-pin row red; the failing child case is the DST one
A1c tomorrow back to today + 86400000 (zone driver) the DST case red same, as predicted
A2 calendar weekStart hard-coded 0 every non-Sunday case red 11 failed / 8 passed: every non-Sunday case, including the 10144 / 10442 heads; en-US and es-MX green
A2b popover weekStartsOn removed es-MX red 1 failed: got lunes, expected domingo
A2c title day back to getDay() === 0 the title-day case red 1 failed, as predicted
A3 CLDR table removed (every region reads Monday) the table-path cases and parity red 5 failed / 7 passed
A3b engine path skipped the -u-fw- and getWeekInfo() cases red 2 failed / 10 passed

Local verification at e4f3430

Every heavy run went through os-verify-lock.sh. The exit codes below are the commands' own, captured before any pipe.

  • Build: pnpm --filter '@object-ui/plugin-calendar^...' --filter '@object-ui/plugin-timeline^...' build exited 0. The doc-snippet gate's scoped closure (turbo run build over its --build-filter) also exited 0: 35 of 35 tasks.
  • Tests: pnpm exec vitest run packages/i18n/ packages/plugin-calendar/ packages/plugin-timeline/ exited 0, with 184 files and 2281 tests passed (145 skipped, the driven zone cases among them). pnpm exec vitest run scripts/__tests__/date-only-zone-pins-10183.test.ts exited 0 with 27 passed. That driver counts a skipped child case as a failure, so the new DST cases ran.
  • type-check for i18n, plugin-calendar and plugin-timeline exited 0. With --listFiles, every test file this change touches is in its package's test program.
  • lint for the same three packages exited 0 (0 errors). The warning counts, by rule, are identical at daa7caf and e4f3430 for CalendarView.tsx, ObjectTimeline.tsx and the i18n entry files, and every new file has 0 warnings.
  • Root gates, each exit 0:
    • check:new-line-citations, check:control-bytes, check:changeset-claims, check:pending-changeset-literals;
    • check:readme-exports (after the scoped build; it judges firstDayOfWeek in the i18n README as real) and check:doc-snippets (779 of 779 blocks judged, 0 failed);
    • check:self-import, check:phantom-deps, check:unused-deps, check:doc-fences;
    • check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:unreferenced-sources, check:esm-specifiers, check:shell-escape-residue;
    • check:dist-completeness, check:published-dist;
    • node scripts/check-changeset-no-major.mjs, check-changeset-presence.mjs, check-changeset-overwrite.mjs;
    • node scripts/check-governed-queue-guard.mjs --test over the 17 changed paths: NOT GOVERNED.
  • NOT MEASURED: check:node-esm-load, reason: its provenance leg refused 8 entries that turbo replayed from another worktree's shared cache (auth, core, data-objectstack, mobile, permissions, providers, react-runtime, types). None of them is in this diff, and none of the three touched packages was refused or listed as failing. A clean reading needs --force-build, which is CI's whole-tree run.
  • Repo-wide pnpm lint and the full pnpm test belong to CI.

Patch round 1: CI red on the parity pin at e4f3430

Cause. CI's node-version: '22.x' floats to the newest Node 22, which ships CLDR 48.0. In CLDR 48 Iceland's week starts on Sunday. The table transcribed CLDR 47, where IS reads the Monday default, and the pin compared it with whatever CLDR the runtime ships. So the pin was green in a CLDR 47 container and red on CI (IS: engine 0, table 1), and updating the table alone would only have moved the red to the CLDR 47 runtime.

Measured runtimes. Three official Node builds were run, checksum-verified against nodejs.org's SHASUMS256.txt: 22.13.0 (CLDR 46.0, the engines floor), 22.22.0 (CLDR 47.0) and 22.23.3 (CLDR 48.0, what CI's 22.x resolves to). Across the three, IS is the only region whose first day differs (Monday, Monday, Sunday).

Shape chosen: a declared release and an exact-equality ledger.

  • utils/first-day-of-week.ts declares the release its table transcribes, WEEK_DATA_CLDR = '48', and the table now carries IS on Sunday. All three rows equal the CLDR 48 measurement, region for region.
  • The pin reads the runtime's release from the major part of process.versions.cldr. The set of regions where the table and the engine disagree must EQUAL RELEASE_DELTAS[release], a ledger in the test whose rows were measured on those runtimes (46: { IS: Monday }, 47: { IS: Monday }). On the table's own release, and on a release the ledger does not list, that set is empty.
  • Why equality, not "every disagreement is listed in the delta": a subset check would stay green on a CLDR 46 or 47 runtime if Iceland were reverted in the table, because the disagreement simply disappears there. Equality goes red for that edit on every runtime, and also when a recorded row stops holding.
  • Why an empty set for an unlisted release: such a runtime is green when it agrees with the table, and red, naming each region and the remedy, when its CLDR moves a first day. That red is the real drift signal. A new release that moves nothing does not go red.
  • Not chosen: skipping the comparison when the release differs. That would turn the pin into a no-op on two of the three supported runtimes.
  • What remains: when CI's Node moves to a CLDR release that moves a first day, this pin goes red until the table or the ledger is updated. That is the drift the round asked to keep catching, and the failure message names both remedies.

Ablations on the new head, each through scripts/ablation-replace.mjs, each with the mutation proven landed and the restore proven (blob == HEAD, git diff HEAD empty), and each run on all three runtimes:

Leg Mutation CLDR 46 CLDR 47 CLDR 48
A4 Iceland removed from the table (back to CLDR 47 content) red: parity and ledger honesty red: parity and ledger honesty red: parity (CI's shape, { IS: 0 } found) and ledger honesty
A6 the ledger's 47 row removed green red: { IS: 1 } found, {} expected green
A3 the CLDR table removed 6 red 6 red 6 red
A3b the engine path skipped 2 red 2 red 2 red

Re-run at a393686 (after merging main at da35453 in 98877dd, a merge commit; no rebase, no force-push):

  • pnpm exec vitest run --maxWorkers=2 packages/i18n/ packages/plugin-calendar/ packages/plugin-timeline/ exited 0 on CLDR 47: 184 files, 2283 passed, 145 skipped. The same suites under Node 22.23.3 (CLDR 48, CI's release) exited 0 with the same counts.
  • firstDayOfWeek-11675.test.ts alone: 14 of 14 passed on CLDR 46, 47 and 48.
  • The zone driver scripts/__tests__/date-only-zone-pins-10183.test.ts exited 0, 27 passed.
  • type-check and lint for the three packages exited 0. The package warning totals are unchanged, and both edited files have 0 warnings.
  • check:new-line-citations, check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check-changeset-no-major, check-changeset-presence, check-changeset-overwrite, check:unreferenced-sources and check:test-path-roots each exited 0.
  • No README, doc or changeset text changed this round, so check:readme-exports and check:doc-snippets were not re-run.

Acceptance notes

  • The DST bound correction rides along. The bucket-bounds block was rewritten as one unit, so tomorrow moved from today + day to a local-calendar step together with the week bounds. It is pinned by the driven DST case, and ablations A1b and A1c show each half failing alone.
  • The gantt week axis is unchanged. It labels plan weeks (Week 1, Week 2) counted from the axis's first day, a documented choice, not a hard-coded weekday. Snapping it to a calendar week would change what the axis means. No card.
  • Other date pickers take date-fns's week start. Pickers outside the calendar, the ones built on the @object-ui/components Calendar primitive without a weekStartsOn, still take date-fns's week start for the tag. For tags date-fns has no locale of its own for (for example es-MX), that is not CLDR's start. Ablation A2b measured the mechanism on the primitive, not through a field's public door. Owner: none. Noted, not filed.
  • The table path reads the region only. A -u-fw- or -u-rg- keyword on the tag is honoured only where the engine has week info. No producer writes those keywords today.
  • The diff goes beyond the claim's listed file surface in docs only. The three READMEs, the two content/docs/plugins pages and the i18n index.ts / utils/index.ts exports are required by AGENTS.md Add automated testing infrastructure and CI/CD workflows #2 or by the helper's own export. They are additive and are named here so the seat can amend the surface.

Generated by Claude Code

claude added 3 commits October 6, 2026 09:16
…cale's first day (objectui#11675)

`@object-ui/i18n` exports `firstDayOfWeek(locale)`, which reads the
engine's `Intl.Locale` week info (`getWeekInfo()` or the `weekInfo`
accessor) and falls back to CLDR's region table where the engine has
neither. The calendar's month grid, weekday heads, week view, header
range and date popover, and the timeline's "This week" / "Next week"
buckets, all start the week there instead of on a hard-coded Sunday
(calendar) or Monday (timeline).

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
…rt in the helper, the calendar and the timeline (objectui#11675)

The helper's engine and table paths, with a region-by-region parity case
against the runtime's week data; every calendar site the week start
moved (month grid and heads, week view and header range, a wrapped
span's title day, the date popover); the timeline's week buckets under
en-US, en-GB, zh-CN and ar-EG; and, in the driven zone pin, the bucket
bounds across a DST change. The two header helpers in the 10144 and
10442 pins took the first day as an explicit argument: they modelled
the Sunday-first heads the grid no longer draws for `de` / `fr-FR`.

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
…k start, and declare the changeset (objectui#11675)

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin tests package: i18n labels Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3323.6 KB 3330.4 KB
Main entry chunk (gzip) 153.5 KB 350 KB
Entry file index-BPrWBVca.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 17.22KB 6.37KB
app-shell (runtime-config.js) 22.52KB 7.86KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 574.72KB 137.94KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 233.72KB 64.83KB
fields (index.js) 262.75KB 66.62KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.18KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.26KB 23.05KB
plugin-chatbot (index.js) 198.81KB 47.14KB
plugin-dashboard (index.js) 143.75KB 38.87KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 247.23KB 65.04KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.09KB 45.89KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 238.48KB 65.51KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 116.72KB 29.10KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.94KB 11.75KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e4f34300f85f1a698d0031964ea7c950ddaaea01
Local-runs: none

Inputs read: card #11675 (body and its four comments: ruling 6010771237, claim 6012945073, os-dev-report 6013850731, seat ACCEPT 6013900089); PR #11710 (body, 17-file list, net diff against main: the three-dot diff from merge-base daa7caf, 17 files, +863/-41; the branch is three commits behind main at da35453, none of which touches a file in this diff); the 42 check-runs on the head. Head-tree facts below were read with git show / git grep on a pinned fetch. Nothing was built, run or re-run.

① Derived judgments

  1. @object-ui/i18n public surface WIDENS by firstDayOfWeek(locale: string): WeekdayIndex and type WeekdayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6. src/index.ts re-exports both from ./utils/index.js, which re-exports ./first-day-of-week.js; the package's types and exports["."] point at dist/index.d.ts, so both names reach the built entry. Neither name exists on main (git grep over packages/*/src answers nothing). Right. The i18n README documents it and README Export Check is green.
  2. @object-ui/plugin-calendar accept-set: unchanged. CalendarViewProps is untouched, so no week-start setting is added, as ruling B requires. MonthViewProps and TimeGridViewProps gain a required weekStart, but both are module-private: the entry src/index.tsx exports ObjectCalendar, CalendarView and their types only, and CalendarView.tsx exports neither view. No public type narrows. Right.
  3. @object-ui/plugin-calendar rendered behaviour (published through CalendarView, and ObjectCalendar through it): every week surface now starts on firstDayOfWeek(effectiveLocale): the month grid's leading days (getMonthDays, which still fills 42 cells for any first day), the weekday heads (reference Sunday plus weekStart), the wrapped-span title day (getDay() === weekStart), the week view's columns and the header's range (getWeekStart(date, weekStart); getDateLabel is unmemoized so it reads the live value), and the popover via weekStartsOn. The memo deps carry weekStart wherever the value is memoized. No hard-coded getDay() / Sunday / Monday site remains in the package's non-test sources. Right.
  4. The popover override is sound and changes no other package: @object-ui/components' Calendar spreads ...props onto DayPicker, so weekStartsOn is react-day-picker's own prop and overrides the date-fns locale's weekStartsOn; localeTag already existed on it. Right.
  5. @object-ui/plugin-timeline accept-set: unchanged (ObjectTimelineProps untouched). Rendered behaviour: "This week" and "Next week" start on firstDayOfWeek(useDisplayLocale()), the hook renderer.tsx already formats with; displayLocale is in the memo deps. @object-ui/i18n was already a dependency and already imported by renderer.tsx and useTimelineTranslation.ts on main, so no manifest change was owed. The bounds block now steps local midnights (daysFromToday), tomorrow included: a DST correction inside the one block the ruling rewrites, pinned by the driven DST case and disclosed in the changeset. Right.
  6. Error surface: a tag Intl.Locale refuses now throws RangeError from firstDayOfWeek at the top of CalendarView's render; toLocaleDateString throws the same RangeError for the same tag in the same render, and useDisplayLocale() returns locale || language || 'en', never empty, so the default path cannot throw. No accept-set change. Right.
  7. Ruling B (6010771237) held: no setting is added, the four retired keys are not read, and the card's pins (en-US Sunday; en-GB and zh-CN Monday), plus ar-EG Saturday, are asserted as literal dates in the three new pin files, not derived from the function under test. The gantt week axis is untouched, and renderer.tsx lines 243-244 document that it counts plan weeks and does not snap to a weekday. Right.
  8. Re-judged pins (CalendarView.gridcellLocale-10144, CalendarView.displayLocale-10442): the weekday-head helpers take the first day as a stated literal and still assert the full seven-head row; the 10144 not.toEqual(weekdayHeaders('default', MONDAY)) control still discriminates German heads from the runtime default's. Not weakened. Right.
  9. Dependents: apps/console, apps/site and packages/app-shell depend on the plugins; app-shell re-exports nothing from them, and all three changed packages sit in the one fixed group. No further changeset is owed. Right.
  10. Check-runs on the head at the last poll before this post: 33 success, 3 skipped (Test coverage, its shard, dependabot: the expected skips), 0 failure, 6 in progress. Green among the concluded: Type Check, Lint, Build & E2E, Test (dist pins), Test shards 2/8 and 8/8, Doc Snippet Type Check, Doc Component Type Check, Changeset Declaration, Changeset Claim Re-read, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report, README Export Check, Governed Surface Queue Guard, Spec Main Shape Gate, Pre-Install Import Graph Check. NOT YET CONCLUDED: Test shards 1/8, 3/8, 4/8, 5/8, 6/8 and 7/8. Their convergence is the seat's landing check, not this record's; this verdict judges ①②③ on the diff and the concluded runs.

② Semver level

  • Changeset .changeset/11675-week-start-from-locale.md: @object-ui/i18n, @object-ui/plugin-calendar, @object-ui/plugin-timeline, all minor. The i18n widening takes at least minor. The two plugins change a published component's rendered behaviour; objectui ships that as minor with the break spelled out, and the changeset spells it: "A host that relied on the calendar always starting on Sunday, or on the timeline's week always starting on Monday, now sees the locale's first day." Each prose bullet matches the diff: the helper's engine-then-table paths, the Monday world default and the RangeError; the six calendar sites and the date-fns es-MX divergence; the timeline bounds and the DST stepping; the gantt axis unchanged. Right.
  • The PR's Clause-② declaration reads yes with no arm, at the start of its second line: a widening, which the i18n export is. By the reader's own rule an absent arm declares no direction; the host-visible change is disclosed in the changeset prose, the card is graded bug, and the fix is the ruling's deliverable, so the plain yes is right. Matches the claim's yes (6012945073) and the seat's reading (6013900089).
  • Gates on the head: Changeset Declaration, Changeset Claim Re-read, Changeset Bump Policy, Changeset Fixed Group Check and Changeset Overwrite Report are all green.

③ Boundary flags

open_questions: none declared by the dev. Every deviation and out-of-scope finding in the os-dev-report, answered:

  1. PR assignee not written (the dev's label-write was refused by its permission classifier): not a diff matter; the seat has raised it to the maintainer. Answered, nothing owed here.
  2. Diff beyond the claim's listed file surface (three READMEs, two content/docs/plugins pages, the two i18n export lines): additive, required by AGENTS.md Add automated testing infrastructure and CI/CD workflows #2 and by the helper's own export; the seat accepted them. Right.
  3. DST bound correction in place: inside the one block ruling B rewrites; pinned by the driven DST case in ObjectTimeline.dateOnlyZone-10866; ablations A1b and A1c each red alone; disclosed in the changeset. Right, a touched published defect corrected in place, not scope creep.
  4. Two existing pins re-judged: judged under ① item 8. Not weakened.
  5. Attribution: the three branch commits end with Claude-Session: plus Co-authored-by: Claude, the repo's own pair, and the branch has no merge commits. Not a contract matter. Acknowledged.
  6. check:node-esm-load NOT MEASURED locally: .github/workflows/node-esm-load-gate.yml runs on workflow_dispatch, nightly at 04:17 UTC, and on pushes to main that touch the gate's own two files; its header calls it an alarm, not a merge blocker, and it is absent from this head's check-runs by design. The specifiers-only leg (check:esm-specifiers) passed locally. Nothing is owed on the PR; the nightly reads it after merge. Answered.
  7. Out of scope, date pickers on the Calendar primitive outside the calendar take date-fns's week start (es-MX reads es, Monday, where CLDR says Sunday; enUS, Sunday, until the locale loads): outside this card's ruled scope, which 6010771237 limits to the calendar and the timeline, but the same principle ruling B states, and ablation A2b measured the mechanism. ESCALATED to the seat: it needs a reach reading at a field's public door before a follow-up card is filed or declined. Not a FAIL reason on this PR.
  8. Out of scope, gantt week axis: the renderer docblock confirms plan-week counting with no weekday snap, so ruling B does not reach it. No card. Right.
  9. Out of scope, -u-fw- / -u-rg- keywords on the table path: honoured on the engine path, stated in the module header, no producer writes them today. No card. Right.

Implemented-by: claude/issue-11675-week-start-from-locale
Reviewed-by: session_01FngvPpdrnhHMdHHq6vwwju

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

CI red on e4f3430: Test (shard 4/8). This PR's own new parity pin fails on CI's CLDR. A patch round is queued for the original dev

domain:ui execution seat 2 · session_01FngvPpdrnhHMdHHq6vwwju · 2026-10-06T10:15Z. The seat does not write code; this records the blocker and its owner.

  • Failing check: Test (shard 4/8) (job 112212108949), and the Test rollup with it. Exactly 1 test failed of 7508:
    • packages/i18n/src/__tests__/firstDayOfWeek-11675.test.ts:184, "PARITY: for every region this runtime names, the table answers what its week data answers".
    • AssertionError: the CLDR table in utils/first-day-of-week.ts no longer matches this runtime's week data (CLDR 48.0); regenerate it from the engine: expected [ 'IS: engine 0, table 1' ] to deeply equal [].
  • It is this PR's, not a flake: the pin is new in this diff. CI's Node ships CLDR 48.0, where Iceland's week starts on Sunday. The PR's table transcribes CLDR 47, where IS falls to the Monday default. The dev's local runtime answered with its own CLDR, so the local run was green.
  • Root cause, a design point: the pin compares a static table against whichever CLDR the runtime happens to ship. It goes red whenever local and CI Node versions differ, in either direction. Updating the table alone moves the red to the other runtime.
  • Next: a patch round on this PR by the same dev, as soon as a batch slot frees. It updates the table to the CLDR the repo's CI runtime ships, and redesigns the parity pin so it is deterministic on every supported runtime while still catching table drift.
    • ⛔ No test is skipped or deleted to get green.
    • The contract review 6014049550 covers e4f3430 only; a code push needs a new at-tier record before enqueue.

Generated by Claude Code

claude added 2 commits October 6, 2026 10:48
…1675 patch round 1)

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
…in expects each runtime's recorded differences (objectui#11675)

CI's floating `22.x` Node ships CLDR 48.0, where Iceland's week starts on
Sunday; the table transcribed CLDR 47, so the parity pin went red there
while it stayed green on a CLDR 47 runtime. The table now declares the
release it transcribes (`WEEK_DATA_CLDR`, 48) and carries Iceland on
Sunday. The pin reads the runtime's release from `process.versions.cldr`
and requires the differences it finds to EQUAL the ones a measured ledger
records for that release (none on the table's own release), so it is
deterministic on CLDR 46, 47 and 48 and still fails on real drift in
either direction.

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3323.7 KB 3330.4 KB
Main entry chunk (gzip) 153.5 KB 350 KB
Entry file index-DuT5x0tj.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 17.22KB 6.37KB
app-shell (runtime-config.js) 22.52KB 7.86KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 574.72KB 137.94KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 233.72KB 64.83KB
fields (index.js) 262.75KB 66.62KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.18KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.26KB 23.05KB
plugin-chatbot (index.js) 198.81KB 47.14KB
plugin-dashboard (index.js) 143.75KB 38.87KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 247.23KB 65.04KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.09KB 45.89KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 238.48KB 65.51KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 116.72KB 29.10KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.94KB 11.75KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a393686c95ec4676c8d5f4fe467f5de74971168a
Local-runs: none

Inputs read: card #11675 (body and its six comments: ruling 6010771237, claim 6012945073, os-dev-report 6013850731, seat ACCEPT 6013900089, patch-round os-dev-report 6015022005, seat ACCEPT 6015076038); PR #11710 (body as corrected by the seat, the 17-file list, the net diff against main at da35453, which is the merge-base: 17 files, +925/-41; the four PR comments, the round-1 record 6014049550 and the CI-red note 6014128933 among them); the 43 check-runs on the head. Head-tree facts below were read with git show / git diff / git grep on a pinned fetch of the branch. Nothing was built, run or re-run. This record judges the whole net diff on this head; the PASS at e4f3430 does not carry.

① Derived judgments

  1. @object-ui/i18n public surface WIDENS by firstDayOfWeek(locale: string): WeekdayIndex and type WeekdayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6. src/index.ts re-exports both from ./utils/index.js, which re-exports them by name from ./first-day-of-week.js; exports["."].types is dist/index.d.ts, so both reach the built entry. Neither name exists on main. Right.
  2. WEEK_DATA_CLDR (new this round, export const in utils/first-day-of-week.ts) is NOT published: utils/index.ts re-exports only the two names above, the entry's type graph does not reach it, and the exports map has no ./utils/* subpath, so the .d.ts that declares it is shipped bytes and not an accept set. The pin imports it by relative path, as five other i18n test files already import ../utils/ modules. Right.
  3. @object-ui/plugin-calendar accept-set: unchanged. CalendarViewProps and CalendarViewEvent are untouched, so no week-start setting is added, as ruling B requires. MonthViewProps and TimeGridViewProps gain a required weekStart, but both are module-private: the entry src/index.tsx exports ObjectCalendar, CalendarView, their prop types and the CalendarEvent alias only, and CalendarView.tsx exports neither view. Right.
  4. @object-ui/plugin-calendar rendered behaviour (published through CalendarView, and ObjectCalendar through it): every week surface starts on firstDayOfWeek(effectiveLocale): the month grid's leading days (daysIntoWeek, still 42 cells), the weekday heads (reference Sunday plus weekStart), the wrapped-span title day (getDay() === weekStart), the week view's columns and the header's range (getWeekStart(date, weekStart)), and the popover via weekStartsOn. weekStart is in every memo's deps. No getDay() === 0 or hard-coded Sunday/Monday site remains in the package's non-test sources. The popover override is sound: @object-ui/components' Calendar destructures localeTag and spreads ...props onto DayPicker, so weekStartsOn is react-day-picker's own prop and outranks the date-fns locale's. Right.
  5. @object-ui/plugin-timeline accept-set: unchanged (ObjectTimelineProps untouched). Rendered behaviour: "This week" and "Next week" start on firstDayOfWeek(useDisplayLocale()), the hook the renderer already formats with; displayLocale is in the memo deps. The bounds block steps local midnights (daysFromToday), tomorrow included: a DST correction inside the one block the ruling rewrites, pinned by the driven DST case and disclosed in the changeset. @object-ui/i18n was already a dependency; no manifest change was owed. Right.
  6. Published behaviour moved by this round, on the TABLE path only (an engine with neither getWeekInfo() nor weekInfo): IS now reads Sunday (CLDR 48) where e4f3430 read Monday (CLDR 47). All other table rows are byte-identical to round one, and the engine path is untouched. The changeset, the three READMEs and the two docs pages name no CLDR release, so the round left every published sentence true. Right.
  7. The parity pin on this head, the round's particular question. The case enumerates every two-letter region the runtime names (asserted above 200, and asserted to reach all four first days CLDR uses), reads the engine's day for each with Intl.Locale week info, reads the table's day for each with both prototype spellings removed, and builds found = the regions where they differ, each mapped to the ENGINE's day. expected is {} on the table's own release (WEEK_DATA_CLDR, 48) and on any release the ledger does not list, and RELEASE_DELTAS[release] on a listed one (46: { IS: Monday }, 47: { IS: Monday }); the comparison is toEqual, exact set equality. So, per supported runtime: on CLDR 48 (what CI's floating 22.x ships, the red at e4f3430) the table must match the engine region for region; on CLDR 46 (the engines floor, Node 22.13 and up) and 47 the set of differences must be exactly { IS: Monday }, so every other region is still compared strictly; on a release newer than the table the comparison is strict and goes red, naming each region and both remedies, only when CLDR actually moves a first day. There is no skip, no runIf, no subset check and no release on which the comparison is vacuous: a table edited away from its release goes red on 48 by the equality and on 46 and 47 by the ledger-honesty case (each ledger row must name a day the table does NOT give); a ledger row that no longer holds goes red on the runtime it describes; a region that moves unrecorded goes red everywhere. The release is read from process.versions.cldr with a rig case that goes red when it is unreadable, and the file is a .test.ts in the unit project (environment: 'node'). The prototype removals are undone in finally and the last case asserts both descriptors are byte-equal to what the file found, which is what isolate: false in that project requires. The pin is not loosened to a no-op on any supported runtime and still catches real table drift. Right. (CI's Test (shard 4/8) on this head is the CLDR 48 reading; the dev's 46 and 47 readings are its report's, and the shape above is what makes them deterministic.)
  8. Error surface: a tag Intl.Locale refuses throws RangeError from firstDayOfWeek at the top of CalendarView's render and inside the timeline's memo; toLocaleDateString throws the same for the same tag in the same render, and useDisplayLocale() never returns an empty tag. No accept-set change. Right.
  9. Ruling B (6010771237) held: no setting is added, none of the four retired keys is read, and the card's pins (en-US Sunday; en-GB and zh-CN Monday) plus ar-EG Saturday are asserted as literal dates in the three new pin files, not derived from the function under test. The gantt week axis is untouched. Right.
  10. Re-judged pins (CalendarView.gridcellLocale-10144, CalendarView.displayLocale-10442): the weekday-head helpers take the first day as a stated literal and still assert the full seven-head row; the 10144 not.toEqual(weekdayHeaders('default', MONDAY)) control still discriminates. Not weakened. Right.
  11. The merge commit 98877dd (parents e4f3430 and da35453) is clean: its combined diff is empty, and none of main's three intervening commits (eb4552e, de96f3d, da35453) touches any of the 17 files. The net diff is the branch's own work. Right.
  12. Dependents: the three changed packages sit in one fixed group and nothing re-exports them; Changeset Fixed Group Check is green. No further changeset is owed. Right.
  13. Check-runs on the head: 43, all concluded. 40 success, 3 skipped (Test (coverage), its shard and dependabot: the expected skips), 0 failure, 0 in progress. Test (shard 4/8), red at e4f3430 on this pin, is green, as are Type Check, Lint, Build & E2E, Test (dist pins), the other seven shards, README Export Check, Doc Snippet Type Check and the five changeset gates. Nothing is left unconcluded for the seat's landing check.

② Semver level

  • Changeset .changeset/11675-week-start-from-locale.md: @object-ui/i18n, @object-ui/plugin-calendar, @object-ui/plugin-timeline, all minor. The i18n widening takes at least minor. The two plugins change a published component's rendered behaviour; objectui ships that as minor with the break spelled out, and the changeset spells it: "A host that relied on the calendar always starting on Sunday, or on the timeline's week always starting on Monday, now sees the locale's first day." Each prose bullet matches the diff on this head: the helper's engine-then-table paths, the Monday world default and the RangeError; the six calendar sites and the date-fns es-MX divergence; the timeline bounds and the DST stepping; the gantt axis unchanged. No major is declared. Right.
  • Clause-②: yes, no arm, at the start of the PR body's second line: a widening, which the i18n export is. The arm's one reader ties narrowing to a narrowed published accept set; no accept set narrows here (① 3, 5), and the host-visible behaviour change is disclosed in the changeset prose as the repo requires, so the plain yes is right. Matches the claim (6012945073) and both seat ACCEPTs.
  • Gates on the head: Changeset Declaration, Changeset Claim Re-read, Changeset Bump Policy, Changeset Fixed Group Check and Changeset Overwrite Report are all green.

③ Boundary flags

open_questions: none in either os-dev-report. Every deviation and out-of-scope finding, this round's first and round one's carried, answered:

  1. Measurement inputs (three official Node builds downloaded and checksum-verified to run the pin on CLDR 46, 47 and 48): reads, not GitHub writes, and not a diff matter. The readings are the dev's; the pin's shape (① 7) is what this record judges. Answered.
  2. Ledger rows measured, not inferred, and the strict reading on an unlisted release: judged under ① 7. Right.
  3. PR body stale after the round: the seat applied the dev's pr_body_patch. Verified on the body as read: WEEK_DATA_CLDR (48), 14 cases, the new parity description, and a "Patch round 1" section. Answered.
  4. check:readme-exports and check:doc-snippets not re-run this round: the round's diff is two files (utils/first-day-of-week.ts, the pin), no README, doc or changeset text, and README Export Check and Doc Snippet Type Check are green on this head. Answered.
  5. PR assignee unset (the dev's label-write refused by its classifier): not a diff matter; raised to the maintainer by the seat. Answered, nothing owed here.
  6. Out of scope, CI's floating node-version: '22.x' can turn any Intl-data pin red on main with no repository change: this pin now tolerates a release that moves nothing and goes red, naming the region and the remedy, on one that does; git grep finds no other reader of process.versions.cldr / .icu under packages or scripts, and other Intl-reading pins were not surveyed. ESCALATED to the seat as a candidate hygiene card (a survey, or a pinned Node minor); not a FAIL reason on this PR.
  7. Observation of this record's own, no verdict weight: the ledger-honesty case asserts rows.length above 0, so the ledger can never be emptied. Once the engines floor passes CLDR 47 a historical row must be kept, or that one line changed. Harmless today; it loosens nothing.
  8. Carried from round one, unchanged on this head and re-read: the DST bound correction in place (inside the one block the ruling rewrites, pinned by the driven DST case, disclosed; right); docs and export lines beyond the claim's listed surface (additive, AGENTS.md Add automated testing infrastructure and CI/CD workflows #2, the seat accepted them; right); attribution (not a contract matter); check:node-esm-load NOT MEASURED locally (a nightly / workflow_dispatch alarm absent from PR check-runs by design; answered); date pickers on the Calendar primitive outside the calendar taking date-fns's week start (outside the ruled scope; escalated in 6014049550 for a reach reading at a field's door; still the seat's); the gantt week axis (plan weeks by documented design; no card); -u-fw- / -u-rg- on the table path (engine path only, no producer; no card).

Implemented-by: claude/issue-11675-week-start-from-locale
Reviewed-by: session_01FngvPpdrnhHMdHHq6vwwju

VERDICT: PASS


Generated by Claude Code

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 package: i18n plugin tests

Projects

None yet

1 participant