Skip to content

Setup notification preferences: draw quiet hours (免打扰) as a time-range control instead of a JSON box (#30208 row 6 step 3) #31421

Closed

Description

@objectstack-fleet

domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ

Filing gate: ② owed by #30208 row 6, step 3 of the recorded steps (6670396132, ruling E on decision #30527, the maintainer's 「同意」 2026-11-05T21:42Z): "Quiet-hours time-range control: its own ui card, not waiting on this ruling". It was not cut until now. ⛔ Not a claim.
Reader: the domain:ui seat that owns the Setup record form widgets (ui/packages/** field renderers).
Dedupe (open AND closed): quiet hours, 免打扰, quiet_hours control → only #30208 and its row-6 children #31062 / #31004 (topic and channel only).
reach: every administrator or user who sets 免打扰 on a notification preference in Setup.
class: a · type: Enhancement
Grade: p3, as its parent #30208.

维护者速读

通知偏好的「免打扰」现在是一个 JSON 输入框,要手写 { "tz": "Europe/Berlin", "start": "22:00", "end": "07:00" }。应当换成一个时间段控件:时区选择 + 开始/结束时间,可跨午夜。存储格式不变。

What to do

  • sys_notification_preference.quiet_hours (packages/services/service-messaging/src/objects/notification-preference.object.ts, Field.json) stores { tz?, start: 'HH:MM', end: 'HH:MM' }, where an absent tz means UTC and the window may run past midnight. Draw it as a time-zone picker plus two time inputs. Write exactly that shape, and read an existing value back into the control. An unreadable stored value shows as-is with a note and is never silently rewritten.
  • If the ui needs the field to declare a widget hint, as the topic and channel fields do since feat(service-messaging): serve the notification topics and channels a preference or template can name #31004, that is a small server half. Name it here and the server lane cuts it. Otherwise this is ui-only.
  • Pins: an empty value writes nothing, a 22:00 to 07:00 window round-trips, and a stored value without tz reads as UTC.

Refs #30208

Activity

  1. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:ui · confirmed · 2026-11-08T07:18Z

    ⛔ Not a claim.


    Generated by Claude Code

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

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo

    Claim: PM loop round 1 · 2026-11-08T07:21Z
    Session: session_01QsR2jTaiww8wGfNrqAW3vs (card thread, dev) · seat session_01QsR2jVEjiYrK7onirxUGyo
    Account: zhuangjianguo
    Branch: claude/project-thread-g0hzfv
    Worktree: objectstack-issue-31421
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: ui/packages/fields/src/** (the quiet-hours widget, its registration, tests), the widget registration sites step 1 names, new keys in ui/packages/i18n/src/locales/*.ts (all ten packs), root .changeset/; ⛔ no packages/services/**, packages/spec/** or server change
    Container & model: S/M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: yes
    Metadata-protocol: none
    Responsibility: n/a — not a defect card (quiet hours is drawn as a hand-written JSON box)
    Thread-read: 6718657041
    Premise: holds: quiet_hours is Field.json with no widget hint in packages/services/service-messaging/src/objects/notification-preference.object.ts and no ui file names quiet_hours on origin/main 216b18a6c
    Serial constraints cleared: none — the only other domain:ui card in pm:dispatched is #31391 (this seat, ui/apps/console/src/pages/settings/**); both add keys to the ten @object-ui/i18n locale packs, so whichever lands second merges main and re-runs key parity

    Scope: "What to do" and "Pins". Step 1 first: if the ui cannot pick the control without a widget hint on the field, the hint is a server-lane half the seat files, and this card waits on it. Clause-②: yes for a new widget name and new locale keys; the dev measures and the seat amends if wrong. PR says Fixes #31421. Brief: /mnt/project-files/issue-31421/brief.md. Summons: the maintainer's 「开一个ui 座位」 (this project, 2026-11-08T06:02Z).

  4. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo

    Claim amendment · 2026-11-08T07:25Z · claim 6718689812

    • Step 1 (dev): the record form picks a widget by form widget, then field.widget, then type, so a json field with no hint goes to the JSON editor. Routes without a hint key on the field name or sniff the value, and neither can draw an empty row. The clean route is a widget: 'quiet-hours' hint on the field, like topic and channel.
    • Server half filed: notification preference: quiet_hours declares the quiet-hours widget hint (server half of #31421) #31426 carries the one-line hint in packages/services/** (server lane). An unregistered name degrades to the JSON editor, so the two halves land in either order. This card builds the control now against a fixture field that declares the hint. The control appears in Setup once notification preference: quiet_hours declares the quiet-hours widget hint (server half of #31421) #31426 lands.
    • File surface widened (the registration sites step 1 named, all under ui/**): ui/packages/cli/src/utils/known-schema-types.ts (regenerated), ui/packages/app-shell/src/utils/paramValueShape.ts (one widgetHintOnly row), and the excluded-bucket pin in ui/packages/plugin-detail/src/__tests__/inlineEditTypeCoverage.test.tsx. Still ⛔ no packages/services/**, packages/spec/** or server change.
    • Time zones: Intl.supportedValuesOf('timeZone') with UTC first (the currency-code-picker precedent). A stored zone that is not listed stays shown.
    • Clause-②: yes and Metadata-protocol: none stand.
  5. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jTaiww8wGfNrqAW3vs

    os-dev-report

    {
      "issue": 31421,
      "status": "done",
      "branch": "claude/project-thread-g0hzfv",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31459",
      "session": "session_01QsR2jTaiww8wGfNrqAW3vs",
      "premise_still_valid": true,
      "summary": "Registered field:quiet-hours in @object-ui/fields (QuietHoursField + quietHours.ts): a time-zone picker (UTC first, then Intl.supportedValuesOf('timeZone') filtered by the spec's iana_time_zone membership test) and two time inputs, labelled as one group. It writes exactly { tz?, start, end } once both times are set and null once both are cleared; a half window or a zone alone writes nothing, and the missing time carries a constraint-validation refusal so the form does not submit. Stored parts are kept verbatim (tz stays absent, a one-digit hour stays one digit); an unlisted stored zone stays shown and marked; any other stored shape is shown as stored with a note and replaced only on an explicit 'Set a time range'. Ten fields.quietHours.* keys in all eleven packs (zh-Hant regenerated). The control goes live in Setup once #31426 declares widget: 'quiet-hours' on the field; either PR can land first.",
      "tests": "QuietHoursField-31421.test.tsx 35/35 at 1db93f4e, covering the three card pins (empty writes nothing; 22:00-07:00 round-trips with strict equality; no tz reads as UTC), a 15-shape read table, the zone list, unlisted zone, same-minute window, unreadable value, and the record form path (form renderer draws a json field declaring the hint as a named group; without the hint it stays the JSON box). Ripple run at 3e7c3da7 (fields/, i18n/, cli/, plugin-detail inlineEditTypeCoverage, app-shell paramValueShape/paramToField/degradeSetTwin-5880/widget-dom-leak-sweep, scripts known-schema-types-derivation-5115 and check-doc-component-types, examples/schema-catalog/test/): 483 files, 9687 passed, 3 failed (widget-aria-invalid-registry-e2e, widget-dom-leak-e2e, group-labelling-declaration lacked the widget), fixed in e18cf4c7 and those files 387/387. Final run at 1db93f4e after merging main 9728ab15 and rebuilding the closure (i18n/, the new test, the fixed pins, paramValueShape, inlineEditTypeCoverage, ui scripts/): 277 files, 7314 passed, 15 skipped, 0 failed. type-check fields, i18n, cli, app-shell, plugin-detail: all Done. Ablation via scripts/ablation-replace.mjs (test imports src, no build in path): 'if (hasStart && hasEnd) {' -> 'if (hasStart || hasEnd) {', anchor 1 -> 0, 4 failed / 31 passed; restored blob == HEAD e0edadb540f6, git diff HEAD empty.",
      "gates": "dispatch-gates --commands at 1db93f4e: 39 families, all exit 0 (37 unlocked node/pnpm check gates rerun at 1db93f4e; ui scripts/ vitest and check:type-check-debt under the verify lock at 1db93f4e); --ran: 39 derived, 39 run, 0 NOT-MEASURED (derived zero). check-partof-closing-keyword on the body: exit 0. check:new-line-citations: 0 new. Tier: no path-derived mandate. CI check-runs: not read yet at report time (in_progress).",
      "line_budget": "24 files, +861/-1 vs origin/main, under 3000",
      "files_changed": ".changeset/31421-quiet-hours-control.md; ui/packages/fields/src/{index.tsx,FieldEditWidget.tsx,widgets/QuietHoursField.tsx,widgets/quietHours.ts,widgets/useFieldTranslation.ts,widgets/__tests__/QuietHoursField-31421.test.tsx,__tests__/group-labelling-declaration.test.ts,__tests__/widget-aria-invalid-registry-e2e.test.tsx,__tests__/widget-dom-leak-e2e.test.tsx}; ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt-BR,ru,zh-Hans,zh-Hant}.ts; ui/packages/app-shell/src/utils/paramValueShape.ts (amendment 6718720700); ui/packages/cli/src/utils/known-schema-types.ts (regenerated, amendment 6718720700); ui/packages/plugin-detail/src/__tests__/inlineEditTypeCoverage.test.tsx (amendment 6718720700)",
      "deviations": "Clause-② copied verbatim from the claim as 'yes'; measured it reads 'yes (widening)' (new locale keys and a new RegisteredFieldWidgetType member, nothing narrowed), the arm common rule 11 names. The changeset carries @object-ui/cli patch for the regenerated known-schema-types list; @object-ui/app-shell is private, no changeset. No README or content/docs page: ui/AGENTS.md #2 asks for one, the sibling notification-topic/channel hints have none, and both paths are outside the surface. Unlocked check gates ran outside the verify lock; tests, builds and type-checks under it.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no GitHub MCP calls; GitHub reads were gh api REST GETs (issue, comments, sibling issues)",
      "api_writes": "2 — relay pr_create (#31459, 12273 bytes read back identical, no assignees); this os-dev-report comment via post-stamped. git push to the card branch is not a REST write.",
      "open_questions": [
        {
          "question": "Clause-② reads 'yes' (claim) while the measured arm is '(widening)'. Amend?",
          "options": [
            "A keep 'yes' as claimed",
            "B seat amends to 'yes (widening)' in the PR body and the dev pushes the changeset line"
          ],
          "recommendation": "B, because common rule 11 names that arm for new @object-ui/i18n locale keys"
        },
        {
          "question": "Document the quiet-hours hint (fields README, content/docs/data-modeling/field-types.mdx) now or with #31426?",
          "options": [
            "A with #31426, together with the two notification hints, once the control is live",
            "B a follow-up docs card for all three preference hints"
          ],
          "recommendation": "A, because the hint only reaches a reader once the server half lands"
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: #31391 · noted, not filed — this seat's other card adds keys to the same ten locale packs; whichever lands second merges main and re-runs all-locales-key-parity"
      ]
    }
  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo

    ACCEPT → PR #31459 comment 6719733544 · 2026-11-08T09:20Z · head 1db93f4e (quiet hours drawn as a time-zone picker and two times; 862 lines, not governed, Fixes #31421). Declaration review PASS 6719575053. Ready and auto-merge armed; landing through the merge queue. The control appears in Setup once #31426 adds the hint.

  7. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo

    Landed · 2026-11-08T09:39Z

    Reading Value
    PR #31459 MERGED 2026-11-08T09:38:37Z, merge commit f67f3b359e1ea937a7fdadd002ffe93ad2405052 on main
    #31421 closed completed by the PR's Fixes; labels enhancement, priority:p3, domain:ui (pm:dispatched removed)

    Delivered: @object-ui/fields draws a field that declares widget: 'quiet-hours' as a time-zone picker plus start and end times, writing { tz?, start, end } or null. Remaining: the field hint on sys_notification_preference.quiet_hours is #31426 (server lane, pm:queue); Setup shows the control once it lands.

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

Metadata

Metadata

Assignees

Labels

domain:uiDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions