Skip to content

docs(jobs): say a job body's default locale and time zone are the server's, so always pass timeZone #29202

Closed

Description

@objectstack-fleet

domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt

立卡门: ① 已落地工作欠的后续(产品文档 content/docs/automation/jobs.mdx),合约复审记录在 PR #29031 ③「Follow-up, not blocking」点名;北极星第 2 条覆盖不足。
业务理由: 写定时任务的开发者用 Intl.DateTimeFormat 不传 timeZone,本地跑与服务器跑结果不同(服务器的默认区域与时区),文档现在不说这一点。不修,任务在部署后按服务器时区输出日期,开发者要靠出错才发现。
动手的读者: domain:server 席,content/docs/automation/jobs.mdx 加一句话,与 #29031 新加的那条 bullet 同处。
查重: always pass timeZone jobs.mdx 0 命中(含已关);最近邻 #28898(沙箱加 Intl.DateTimeFormat,已由 #29031 关闭)。

Follow-up of: #28898 (landing PR #29031)

What is missing

#29031 made Intl.DateTimeFormat available in hook, action and job bodies, answering with the host's own Intl. A formatter built with no locale or timeZone therefore uses the server's default locale and time zone, and resolvedOptions() reports them. The contract review kept that on purpose (a lowered handler must answer as its in-process original, and the sandbox's Date already runs in the host zone) and asked for one sentence in the docs instead.

Acceptance

  • jobs.mdx, next to the Intl.DateTimeFormat bullet, says the default locale and time zone are the server's and that a body should pass timeZone (and a locale) for output that does not depend on where it runs.
  • Docs only; no code change. Run the docs gates the PR derives.

Generated by Claude Code

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p3 · domain:devx · pm:queue · 2026-11-02T09:56Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2gLghEqeDKTbDTC7Zd

    Claim: PM loop round 3
    Session: session_017ihL2gLghEqeDKTbDTC7Zd
    Account: marchtian
    Branch: claude/issue-29202-jobs-intl-default-zone
    Worktree: objectstack-issue-29202
    Domain: domain:devx
    Seat: domain:devx#2
    File surface: content/docs/automation/jobs.mdx (one sentence beside the Intl.DateTimeFormat bullet #29031 added)
    Container & model: S fix, mode:subagent, model: default tier, effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the jobs page does not say that a body's Intl.DateTimeFormat defaults to the server's locale and time zone
    Thread-read: 6574224592
    Premise: holds
    Serial constraints cleared: none (no open PR touches automation/jobs.mdx at claim time; checked before dispatch)

    Triaged p3 by triage#1 (6574224592). Docs only, with no code change. The sentence is read off the sandbox's Intl wiring at the current head before it is written.

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2gLghEqeDKTbDTC7Zd
    os-dev-report

    {
      "issue": 29202,
      "status": "done",
      "branch": "claude/issue-29202-jobs-intl-default-zone",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29355",
      "head": "7dc9c8c1ee73220c02e42ca54d976bcb68154e0c",
      "session": "session_017ihL2gLghEqeDKTbDTC7Zd",
      "premise_still_valid": true,
      "summary": "Added two sentences and one example to the Intl.DateTimeFormat bullet in content/docs/automation/jobs.mdx: a formatter built with no locale or timeZone uses the server's default locale and time zone (resolvedOptions() reports them), so pass timeZone and a locale for output that does not depend on where the body runs, e.g. new Intl.DateTimeFormat('en-US', { timeZone: 'UTC' }). Docs only, no code change. Draft PR #29355, label skip-changeset, no reviewers, no assignees.",
      "files_changed": ["content/docs/automation/jobs.mdx (+5 / -0, lines 199-203)"],
      "line_budget": "order: one or two sentences beside the bullet. Delivered: 2 sentences + 1 inline example, +5 / -0, one file. Within budget.",
      "claims": [
        {
          "statement": "With no locale or timeZone, a formatter uses the server's default locale and time zone",
          "backed_by": "packages/runtime/src/sandbox/sandbox-intl.ts: the VM-side DateTimeFormat constructor snapshots locales/options (snapLocales/snapOptions pass undefined through); readSnapshot returns locales undefined and options undefined for an empty snapshot; installSandboxIntl's formatterFor builds new Intl.DateTimeFormat(locales, options) with the HOST's Intl, so ECMA-402 host defaults apply. packages/runtime/src/sandbox/quickjs-runner.ts execute() calls installSandboxIntl(vm) once per invocation for every body kind (hook, action, job, L1 expression).",
          "measured": "scratch probe (not committed): installSandboxIntl into a fresh QuickJS context, evaluate new Intl.DateTimeFormat().resolvedOptions() in the VM. TZ=Asia/Tokyo + de_DE gives de-DE / Asia/Tokyo; TZ=America/New_York + de_DE gives de-DE / America/New_York; TZ=Asia/Tokyo + en_GB gives en-GB / Asia/Tokyo. Each equals the host process's own default resolvedOptions(). All 3 runs exit 0."
        },
        {
          "statement": "(its resolvedOptions() reports them)",
          "backed_by": "sandbox-intl.ts host callback: op 'resolvedOptions' answers f.resolvedOptions() from the host formatter built from the same snapshot",
          "measured": "same probe, the locale and timeZone fields of resolvedOptions()"
        },
        {
          "statement": "so the same body can print a different date on your machine than on the server",
          "backed_by": "follows from the first claim",
          "measured": "same probe: new Intl.DateTimeFormat().format(0) answered 1.1.1970 under Asia/Tokyo and 31.12.1969 under America/New_York (both de_DE); the pinned formatter answered 1/1/1970 in all three settings"
        },
        {
          "statement": "Pass timeZone, and a locale, for output that does not depend on where the body runs: new Intl.DateTimeFormat('en-US', { timeZone: 'UTC' })",
          "backed_by": "same wiring: an explicit locale and timeZone reach the host formatter through readSnapshot's DATE_TIME_FORMAT_OPTION_KEYS copy, so host defaults no longer apply",
          "measured": "same probe: the pinned formatter's resolvedOptions() was en-US / UTC under all three host settings"
        }
      ],
      "tests": "No test owed (docs only, no tested behaviour changed). The measurement above ran with tsx on a scratch script outside the repo; the repo tree carries only the one doc change.",
      "gates": "Derived on the final diff at 7dc9c8c1ee (after merging origin/main a2fcede85c): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack :: exit 0, 43 commands. All 43 run, exit captured before any pipe: 39 exit 0 first time; 4 exit 3 PREREQUISITE NOT MET (check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift: lint/formula/client-react not built), then built under os-verify-lock with pnpm --workspace-concurrency=2 --filter '@objectstack/lint...' --filter '@objectstack/client-react...' build (VERDICT command-exit 0) and each re-run :: exit 0. spec built first under the lock (VERDICT command-exit 0). --ran reconciliation :: exit 0, '43 derived, 43 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero - all 43 recorded an exit code)'. NOT MEASURED locally, CI's: ci.yml Build Docs, ci.yml Plan Test Core shards, type-check lanes. PR check-runs: in_progress at report time, not waited on.",
      "dogfood_battery": "n/a - no ! in the PR title and no (narrowing) arm",
      "deviations": [
        "The probe exercised installSandboxIntl directly on a fresh QuickJS context, not through QuickJSScriptRunner.run(): the runner path needs @objectstack/types built, which a docs card did not otherwise owe. execute() makes the same single call, so the wiring measured is the one bodies get.",
        "The merge of origin/main was committed with git's default message and reworded locally before the push to carry the trailer pair; nothing pushed was rewritten."
      ],
      "mcp_calls": "0",
      "api_writes": "2 relay writes: (1) fleet-write dispatch POST /repos/objectstack-ai/objectstack/dispatches, run 41447967187, executed pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft) and its labels leg (POST /repos/objectstack-ai/objectstack/issues/29355/labels, skip-changeset); read-back identical, 4556 bytes. (2) this comment via scripts/pm/post-stamped.mjs --comment=29202. git push x3 (not REST).",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · noted, not filed · hook and action pages (content/docs/automation/hooks.mdx, content/docs/automation/hook-bodies.mdx, content/docs/ui/actions.mdx) do not carry the Intl.DateTimeFormat bullet at all, so there is no copy to align; jobs.mdx already says hook and action bodies get the same formatter.",
        "carrier: none · noted, not filed · packages/spec/src/system/job.zod.ts JobSchema.handler describe (regenerated into content/docs/references/system/job.mdx) says Intl.DateTimeFormat with a timeZone option is available but not that the default zone is the server's. Accurate as written; generated, so not edited here."
      ]
    }
  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2gLghEqeDKTbDTC7Zd

    Landed · 2026-11-02T13:50Z · PR #29355 merged as 01386412b8 (reviewed head 7dc9c8c1ee, seat record ACCEPT on the PR). The jobs page's Intl.DateTimeFormat bullet now says that a formatter with no locale or timeZone uses the server's defaults. It tells the author to pass timeZone and a locale for output that does not depend on where the body runs. Both statements were read off sandbox-intl.ts and probed under three host settings. pm:dispatched is removed in this stroke.

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

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationdomain:devxDomain lane — seat card indexed by label:pm:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions