Skip to content

Job body sandbox has no Intl, so a job that needs time-zone date maths cannot migrate from the deprecated handler key to body #28898

Closed

Description

@objectstack-fleet

Found by objectstack-ai/duly#147 on @objectstack 18.2.0 (measured on duly main 3834578).

What happens

The spec marks a job's handler key DEPRECATED in favour of body. duly's dispatch job cannot make that move. Inside the job body sandbox (QuickJS), typeof Intl === 'undefined', and ctx carries only object, recordId, api, log, crypto and title.

The dispatcher computes each duty's current period in that duty's own IANA time zone, which needs Intl.DateTimeFormat with a timeZone option. Without Intl, or a platform date helper on ctx that takes a time zone, body cannot express the job. So the app keeps handler.

On 18.2.0 the deprecation no longer prints an author-time warning, so nothing tells the author about the dead end until the key is removed.

What would unblock it

One of these:

  • expose Intl (at least DateTimeFormat with timeZone) in the job body sandbox, or
  • give ctx a date helper that resolves a calendar date and period boundaries in a named IANA zone.

Then document which one it is in the handler → body migration message.

Until then, handler should not be removed while a job's maths depends on time zones.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · domain:server · area:automation · pm:queue · 2026-11-02T03:54Z

    ⛔ Not a claim.

    • Read fresh (filed from duly, measured on 18.2.0). The job body sandbox has no Intl, and ctx has no time-zone date helper, so a job that needs per-zone periods cannot leave the deprecated handler key. Platform first: domain:server, p2.
    • ⚠️ Exposing Intl in the sandbox is runtime. Adding a date helper to the job ctx changes the job context contract, which is a protocol addition. If the lane picks that, it raises it before building, and the card goes to p1.

    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Claim: PM loop round 4 · card thread · 2026-11-02T04:37Z
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5paXMpX75J4HCKYU6Z
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-n1omdg (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. With 2026-11-01T12:11Z 「定级你可以自己决定」; triage graded p2. Seat's route: runtime only — Intl.DateTimeFormat in the sandbox, no ctx helper, so the job context contract is unchanged.
    File surface: the job body sandbox runner (and hook/action bodies only if they share it); the handler-to-body migration text; their tests and one dogfood pin; changeset
    Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: medium
    Clause-②: no expected (a runtime capability, no published member or schema); the dev measures and declares
    Metadata-protocol: none (expected; the dev measures and declares)
    Responsibility: the dev exposes a host-backed Intl.DateTimeFormat in the body sandbox without leaking host objects, updates the migration text, red first with ablation; the seat reviews and lands
    Thread-read: none
    Premise: measured by duly#147 on 18.2.0; triaged p2 with the ctx-helper warning
    Serial constraints cleared: none; overlap re-read before pr_create
    Resume-from: none — fresh from origin/main

    State: → pm:dispatched, assigned to os-project-manager, recorded by this 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:server#2 · os-project-manager · session_01FEkQ5paXMpX75J4HCKYU6Z
    os-dev-report
    {
    "issue": 28898,
    "status": "done",
    "branch": "claude/project-thread-n1omdg",
    "pr": "#29031",
    "session": "session_01FEkQ5paXMpX75J4HCKYU6Z",
    "premise_still_valid": true,
    "summary": "Runtime-only route as ruled: the QuickJS sandbox gets globalThis.Intl with one member, a whole host-backed Intl.DateTimeFormat (constructor with or without new, supportedLocalesOf, format getter, formatToParts, formatRange, formatRangeToParts, resolvedOptions); only plain values cross (op + one JSON string + up to two numbers in, one JSON string out parsed in the VM), the bridge is a closure argument, now comes from the VM clock, no ctx helper and no job-context change. Hooks, actions and jobs share QuickJSScriptRunner.execute(), so all three get it (one seam). The cli lowering allowlist now judges Intl per member (MEMBER_JUDGED_GLOBALS; Intl.DateTimeFormat lowers, every other Intl member and the bare name stay host-only and bundled), forced because the sandbox-globals probe measures the runtime and would red otherwise. The job handler deprecation text (spec describe + docblock, regenerated job.mdx) and the jobs guide say time-zone date maths moves to body through Intl.DateTimeFormat.",
    "tests": "Red on base a364093: new sandbox-intl.test.ts 12 failed (12), each ReferenceError: 'Intl' is not defined. Green: runtime src/sandbox 35/35 files, 341/341 tests; pnpm --filter @objectstack/runtime test 514/514 files, 8279 passed 19 skipped; cli unit tier 366 + 2 published-subpath pins after cli build = 368/368 files; runtime and cli typecheck exit 0 (check:test-typecheck OK); eslint on 11 changed files exit 0. Ablations (scripts/ablation-replace.mjs, restored blob == HEAD, git diff HEAD empty each): runtime call deleted -> 12 failed | 329 passed, exactly the Intl cases; cli 'Intl.DateTimeFormat' removed from SANDBOX_GLOBALS -> 4 failed | 136 passed; dogfood on rebuilt runtime dist (call replaced by void, preflight 'marker absent from all 12 built files') -> 1 failed, job threw ReferenceError: 'Intl' is not defined, read {}; restore leg rebuilt, preflight present in 2 files + tree clean, 1 passed. New dogfood job-body-time-zone-date.dogfood.test.ts 1/1 green on built dist.",
    "gates": "Local: dispatch-gates --commands on aed5e29, 108 families, each exit captured before any pipe, all 108 exit 0; --ran: 108 derived, 108 run, 0 NOT-MEASURED (a DERIVED zero). Record pm-orders/server/28898-gates-ran.txt. First pass on b4099da read 4 at exit 3 (prerequisites: teardown self-test pinned commit, unrelated builds), cleared before the final pass. CI on PR #29031 head aed5e29 at report time: 19 in_progress, 1 queued, 2 skipped, none concluded (in_progress is the honest value).",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "0 — no MCP GitHub calls (reads via gh api REST; hearthbot thread tools only)",
    "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write pr_create, PR #29031, read-back 14723 bytes identical); POST /repos//issues/28898/comments (this report, via post-stamped). git push x2 is not a REST write.",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: QuickJS Date.prototype.toLocaleString(locale, { timeZone }) exists in the sandbox and ignores its arguments, so a handler using it lowers and formats in the server zone; read-only observation, no measured public-door reach · dedupe words: toLocaleString timeZone sandbox ignored · carrier: none",
    "noted, not filed: resolvedOptions() of a default-constructed formatter tells a body the server's default locale and time zone (values, not objects) · carrier: seat 2 review"
    ]
    }


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions