Skip to content

docs(core): say what formatMeasureDate's Date.parse guard actually rejects (objectui#8263) - #10027

Merged
os-tesla merged 1 commit into
mainfrom
claude/issue-8263-measure-date-impossible-calendar
Sep 19, 2026
Merged

os-tesla merged 1 commit into
mainfrom
claude/issue-8263-measure-date-impossible-calendar

Conversation

@os-tesla

Copy link
Copy Markdown
Collaborator

Fixes #8263

Comment-only repair in @object-ui/core's dataset-format.ts. No behaviour change of any
kind.
A mechanical pass over git diff -U0 finds no added or removed line that is not a
comment; the executable body of formatMeasureDate is byte-identical to origin/main.

What was wrong

Two sentences in this file asserted that a well-shaped impossible calendar date is unparseable
and therefore keeps falling through to String(v). Re-measured on origin/main 4dbab84d4,
取数时刻 2026-09-19T13:07:59Z, TZ=UTC, node v22.22.2:

reading value
Date.parse('2026-02-30') 1772409600000 — not NaN ⇒ renders 2026-03-02
Date.parse('2026-02-30T09:30') 1772443800000 — not NaN ⇒ renders 2026-03-02T09:30
CONTROL Date.parse('2026-13-01') NaN ⇒ genuinely falls through
CONTROL Date.parse('2026-13-01T09:30') NaN ⇒ genuinely falls through
CONTROL Date.parse('2024-07-04T99:99') NaN ⇒ genuinely falls through

The controls fire, so the guard is alive — it simply never rejected a DAY that overflows its
month. ECMAScript's Date Time String Format accepts DD in 01-31 syntactically and
MakeDay rolls the surplus into the next month. What the guard rejects is an out-of-range
MONTH, which both patterns admit.

Before / after — every sentence that changed

1. The ISO_DATETIME_RE docblock

Before:

 * `HH:mm`; `Date.parse` decides the rest, so a well-shaped impossible instant
 * still falls through untouched.

After:

 * `HH:mm`; `Date.parse` decides the rest. What it rejects is an out-of-range
 * MONTH or CLOCK reading (`2026-13-01T09:30`, `2024-07-04T99:99`), which this
 * pattern admits — such a value falls through untouched.
 *
 * ⛔ A DAY that overflows its month does NOT fall through. `2026-02-30T09:30`
 * parses and renders rolled over, exactly as the date-only arm renders
 * `2026-02-30`. See the note inside {@link formatMeasureDate} for the
 * instrument that holds that render in place, and for where the open question
 * about it lives.

2. The inline note inside formatMeasureDate

Before:

  // `Date.parse` guards both arms so a well-shaped impossible date
  // (`2026-02-30`) is NOT swallowed into the em dash `formatDate` returns for
  // an unparseable value — it keeps falling through to `String(v)`, exactly as
  // it does today. Only a value that is genuinely a date changes.

After:

  // `Date.parse` guards both arms, and what it rejects is an out-of-range
  // MONTH (`2026-13-01`) — which both patterns above admit — ⛔ NOT an
  // out-of-range DAY. ECMAScript's Date Time String Format accepts `DD` in
  // `01`-`31` syntactically and `MakeDay` rolls the surplus into the next
  // month, so a well-shaped impossible date (`2026-02-30`) is PARSEABLE on
  // both arms: it does not fall through to `String(v)`, it renders as the
  // rolled-over day (`2026-03-02`). This paragraph asserted the opposite —
  // that such a value keeps falling through — until objectui#8263 measured it.
  //
  // ⭐ That rolled render is not a leak in this guard; it is pinned, by
  // `agrees with the list cell on a rolled-over date instead of second-guessing it`
  // in this file's co-located date suite. Read that test for the argument. It
  // is deliberately NOT restated here: a comment restating a pin is how this
  // paragraph came to assert a behaviour that nothing implemented.
  //
  // ⚠️ Whether the SHARED display path should refuse an impossible calendar
  // day at all is OPEN, and it is not this function's to answer alone — the
  // authoring boundary already refuses such a value while the display path
  // rolls it. objectui#10026 carries that question.

⭐ Why this is the prose half and not the code half

The first dispatch on this card asked for the opposite repair — make the code fall through, as
the old comment promised. That direction was withdrawn by the dispatching seat after measurement
showed the file carries a second declaration which says the opposite and which, unlike the
prose, is enforced: the green pin agrees with the list cell on a rolled-over date instead of second-guessing it, whose own comment ends 「Pinned because it is the one place agreement looks
like a bug.」

Two readings made the choice:

  1. The dispatched predicate does not transfer to the date-time arm. new Date(v).toISOString().slice(0,10) === v
    compares a ten-character slice against the whole string, so it answers false for the real
    instant 2026-02-28T09:30:00.000Z (control 2026-02-28 answers true, so the probe is
    alive). Triage required both arms be handled; that spelling cannot handle one of them.
  2. Applying a rejection here alone re-creates objectui#4576 on purpose. formatDate is the
    one shared path behind every date cell, grid card and gantt tooltip, and it renders
    2026-02-30 as Mar 2. A measure tile reading 2026-02-30 beside a list cell reading
    Mar 2 for the same stored string is exactly the divergence this file's header exists to
    prevent — measured, not assumed, against a byte-identical date-display.ts
    (git hash-object = 6cbabb21ccb0eb105722aaaf87b5185b3bfebd2c).

So the behaviour stays and the prose becomes true. Whether the shared path should refuse an
impossible calendar day is a product decision about every date surface in the console, and it is
carried separately by objectui#10026 — which also records the asymmetry that turned this up: the
authoring boundary already refuses such a value (isRealCalendarDate in the filter builder,
「A date that exists — the pattern alone would accept 2024-02-31.」) while the display path
rolls it.

Validation

run result
pnpm exec vitest run packages/core/src/utils/__tests__/dataset-format.date.test.ts Test Files 1 passed (1), Tests 23 passed (23), exit 0
⭐ same suite, same count, on origin/main before this branch Tests 23 passed (23), exit 0
pnpm --filter '@object-ui/core^...' build then pnpm --filter @object-ui/core run type-check VERDICT command-exit 0
node scripts/check-changeset-presence.mjs exit 0 — empty frontmatter, the explicit "releases nothing" declaration
pnpm check:new-line-citations VERDICT new-cross-file-line-citations: 0 new citation(s)
pnpm check:control-bytes OK (scanned 8079 tracked text file(s))
pnpm check:pending-changeset-literals exit 0
node scripts/check-governed-queue-guard.mjs --test … NOT GOVERNED — 2 path(s) checked

The control that matters for a prose-only change is that nothing else moved: the suite
returns the same 23 passing tests it returned on origin/main before this branch existed. The
pin is untouched — it is not in this diff.

The first type-check attempt exited 2 with TS6305 ("Output file … has not been built"), which
is a missing dependency build and not a type error; the row above is the re-run after
pnpm --filter '@object-ui/core^...' build.

Acceptance notes

  • check:changeset-claims is report-only and it asked one question of this change: a pending
    changeset describes dataset-format.ts, claiming 「No second date formatter was written」 and
    that formatDate/formatDateTime/formatRelativeDate MOVED into @object-ui/core. Read
    and still true
    — this diff adds no formatter, moves nothing, and changes no behaviour.
  • check:comment-mask-corpus reports one disagreeing file, apps/console/src/pages/DocsIndex.tsx,
    pre-existing and inside the residue objectui#7882 holds open. Not this diff.
  • noted, not filed: the pin's own comment asserts 「Every date surface in the repo builds its
    Date the same way」 — a live-sounding claim with no instrument behind it (commandment 完善设计器的每一个细节 #9). I
    measured it true today (the date cell renderer routes to formatDate; ObjectGrid imports
    formatDate; formatDate('2026-02-30') is Mar 2), but nothing re-derives it. 承接者:
    whoever rules objectui#10026 — same paragraph, same file.
  • noted, not filed: isRealCalendarDate lives in the React package @object-ui/components
    while the display path lives in @object-ui/core, so core cannot reach it. Not a defect today;
    it is the precondition that makes any future shared-path repair a MOVE rather than a second
    copy. 承接者: objectui#10026.
  • The claim comment on this card carries no Clause-② line, so there was none to copy into this
    body. The changeset decision is stated explicitly instead: empty frontmatter, because a
    comment-only edit publishes nothing.

⛔ Landing

Draft on purpose. ⛔ Not to be marked ready, enqueued or auto-merged by me — landing is the
domain:ui#2 execution seat's act.


Generated by Claude Code

…rejects

`formatMeasureDate`'s inline note and the `ISO_DATETIME_RE` docblock both
declared that a well-shaped impossible calendar date such as `2026-02-30` is
unparseable and therefore "keeps falling through to `String(v)`, exactly as it
does today". Measurement says the opposite, on BOTH arms: ECMAScript's Date
Time String Format accepts `DD` in `01`-`31` syntactically and `MakeDay` rolls
the surplus into the next month, so `Date.parse('2026-02-30')` returns an
instant, not `NaN`, and the value renders as `2026-03-02`. What the guard
actually rejects is an out-of-range MONTH (`2026-13-01`), which both patterns
admit.

Comment-only. No behaviour changes, no guard is added, and the pin that holds
the rolled render in place is untouched and still green.

The new prose cites that pin by its test name rather than restating its
conclusion — a comment restating a pin is how the old paragraph came to assert
a behaviour that nothing implemented — and points at the card carrying the open
question of whether the shared display path should refuse an impossible
calendar day at all.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/7178-format-measure-date.md

  • names dataset-format.tspackages/core/src/utils/dataset-format.ts — edited by this change

    No second date formatter was written. formatDate, formatDateTime, formatRelativeDate and DateDisplayOptions MOVED from @object-ui/fields' barrel down into @object-ui/core (utils/date-display.ts), which is the same remedy objectui#4576 applied to formatDisplayNumber and for the same reason: core is the React-free engine and could not import from a React package, so the alternative was a parallel date convention in dataset-format.ts — exactly the drift that once had a list cell rendering 1.234,5 % beside a dashboard measure's 1.234,5%. @object-ui/fields re-exports all four names unchanged, so no consumer's import path or behaviour changes, and a reference-identity test pins that the cell renderer and the measure formatter call the same function.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with 4dbab84d4 (merge-base with origin/main): 1 file(s) changed outside .changeset/, read against 1212 pending declaration(s) that publish a body (1771 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3056.5 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DqbQb7iW.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) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 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) 545.92KB 130.72KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 216.90KB 60.15KB
fields (index.js) 249.62KB 63.02KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
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) 2.53KB 0.85KB
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.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.73KB 20.08KB
plugin-chatbot (index.js) 198.20KB 47.14KB
plugin-dashboard (index.js) 132.96KB 35.17KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 255.18KB 66.49KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 139.56KB 35.40KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 213.44KB 58.21KB
plugin-kanban (index.js) 48.71KB 15.17KB
plugin-list (index.js) 113.55KB 27.99KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.68KB 8.95KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.18KB 21.05KB
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.66KB 3.50KB
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) 109.04KB 36.08KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.38KB 1.98KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.74KB 2.54KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 15.71KB 5.30KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
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) 0.20KB 0.18KB
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) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
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) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
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

Copy link
Copy Markdown
Collaborator Author

⚠️ Test (shard 1/4) 红了,而它不是这张 PR 的 —— 根因已立卡(objectui#10006),这是它的第 3 次实例

domain:ui#2 执行席(session session_018HrVaotisyhgmot9o2MLRq),2026-09-19T13:39Z。
⛔ 本节不写「flake」:下面是逐层落到一行源码上的根因。

失败读数(取自 job 日志正文,⛔ 不取聚合)

运行 35445378462 / job 105903288520,Test (shard 1/4),1 failed | 11426 passed | 2 skipped:

AssertionError: no listing line for worktree-rule-card-ref under
  .claude/hooks/guard-main-checkout-bash.sh: expected undefined to be truthy
 ❯ scripts/__tests__/upstream-port-parity-wiring.test.ts:280:71

⭐ 为什么它不可能是本 PR 的

本 PR 的文件清单是 2 个:packages/core/src/utils/dataset-format.ts 与一条 .changeset/ 条目。
而且它是纯注释改动 —— 本席在 git diff -U0 上机械验过:34 行改动,经注释过滤后剩 0 行
(控制:未过滤计数非零)。⇒ 一个不含任何可执行改动、且只碰 packages/core 的 diff,
⛔ 不可能改变一条读 .claude/hooks/** 端口平价清单的断言。

⭐⭐ 根因,读在源上(objectui#10006 记的就是它)

失败的断言坐在 it('--list publishes each divergence at its real size…') 里,
其输入来自同一块的 execFileSync('node', [GATE, '--list'], { cwd: ROOT, encoding: 'utf8' })
stdout 是一个管道。而 scripts/check-upstream-port-parity.mjs
if (isEntrypoint(import.meta.url)) { 之后直接 process.exit(…)

⇒ ⭐ process.exit 不 flush 管道上的缓冲 stdout--list 是一个纯 console.log 循环,
输出越长、机器越忙,被截掉的尾巴越多 ⇒ 某一条 - <id> ( 行随机消失 ⇒ find 返回 undefined
expect(line, 'no listing line for …').toBeTruthy() 红。

⇒ 这是一条负载相关的截断,⛔ 不是这张 PR、也 ⛔ 不是任何一张 PR 的内容问题;
它会随机把无关的 PR 弄红。已立卡 objectui#10006,本条是本席量到的第 3 次实例。

处置 —— 按本席章程的顺序,逐条

  1. ⭐ 本条实例已记在 objectui#10006 上。
  2. 没有现成的修复可以搬进来:objectui#10006 至今无人答、无对应 PR ⇒ 无可移植的 fix。
    ⭐ 本席不在这张 PR 上顺手修它 —— 那会把一张纯注释卡扩成一张门禁卡,而根因住在另一条工具链上。
  3. ⇒ 本 PR 花掉那一次允许的重跑(⛔ 之后不再重跑)。若重跑后仍红,那就不是这条截断,
    届时按新读数重新根因。
  4. ⛔ 永不为了让 CI 重跑而推空提交、⛔ 永不关掉再开、⛔ 永不跳过或禁用任何测试。

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants