Skip to content

feat(web-forms): a guest cannot open a Critical case; guest-facing support form labels and hotline hint (#2368) - #2378

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-45r09q
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-45r09q

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread
Fixes #2368
Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 公开支持表单(/forms/support)不再让访客选择优先级,网页工单按默认「低」进入;即使有人绕过表单直接提交「严重」,应用也最多存为「高」,不会触发升级和服务经理通知。表单顶部加了「紧急故障请拨打服务热线。」,姓名和邮箱字段改为「您的姓名」「您的邮箱」(英、中、西、日四种语言)。服务手册和工单文档同步说明。
  • 为什么改: Irene 已裁定访客最多只能选到「高」。18.2.0 的公开表单做不到按表单单独限制下拉选项(已实测:表单行上的 options 子集会被下发,但页面仍列出全部四个选项),所以按裁定的后备方案把优先级字段从公开表单拿掉。
  • 风险与回滚: 低。只改 web_to_case 表单、工单 beforeInsert 钩子的访客分支(仅匿名提交)、语言包与文档;员工创建的严重工单照常升级。不改权限集、岗位、共享规则。回滚即 revert 本 PR。
  • 先看: src/service/objects/case.hook.ts(第 212 行,访客分支里的 critical → high)、src/service/views/case.view.ts(web_to_case 表单:去掉 priority、热线提示、访客标签)、test/web-to-case-guest-priority.test.ts(新)。
  • 两分钟验证: pnpm exec vitest run test/web-to-case-guest-priority.test.ts,预期 5 个用例全部通过;或未登录打开 /_console/f/support,应看到没有「优先级」字段、顶部有热线提示。
  • 需要你做的: 无,席位落地。

What changes

Every row was measured on origin/main 495bff5 (@objectstack 18.2.0) before it was edited.

Row Verdict Where
Guest priority: can a public form restrict a select's options for that form alone? No, on 18.2.0. A form row options: [low, medium, high] on web_to_case.priority parses, and GET /api/v1/forms/support serves it, but the public renderer at /_console/f/support still listed Low, Medium, High, Critical (screenshot probe-form-option-subset-ignored-en.png). The door validates a posted value against the object's options only (@objectstack/rest 18.2.0 registerFormEndpoints filters keys by the form's field list and checks required, nothing else). Platform finding, below. n/a
Guest priority: the ruling's fallback done. priority is off the public form. The door's allow-list now drops a posted priority, and the case takes the object default low. src/service/views/case.view.ts:550 (comment where the row was)
The anonymous door with priority: critical Before: 201, stored critical, status: escalated, is_escalated: true, escalation_reason: "Auto-escalated: critical priority", rank 4, 4-hour SLA. After: 201, stored low, status: new, not escalated, rank 1. live dev boot, readings under Verification
Door hardening, for the day the form asks again capped, not refused. The guest branch of case_sla_defaults turns a guest's critical into high before the rank, the SLA row and every after-insert flow read it. Capped because the visitor's case is real: refusing it loses the case to save a staff re-triage, and the hotline hint covers a real P1. Measured with the subset form still in place: the door answered 201 and stored high, not escalated. src/service/objects/case.hook.ts:212
Escalation paths on that insert Nothing else fires. case_escalation_on_create starts only on priority == "critical" (no run row for a capped case); the service-manager hand-off rides on the escalation; Escalated Cases Sharing and the dashboard tile read critical; the SLA row is read after the cap (high × smb). Tested: no run, no notification to a service_manager holder, owner not handed to the pool. test/web-to-case-guest-priority.test.ts
Staff side Unchanged. A case an agent creates at Critical stores critical and escalates (tested here and in flow-record-change). same test file
Hotline hint done as the section description, the one help-text slot every visitor reads now that the priority row is gone: "For an urgent outage, call the service hotline." / 「紧急故障请拨打服务热线。」, plus es-ES and ja-JP. The old priority help text left with the row. case.view.ts:521, four packs _sections.how_can_we_help.description
Row 1 · "Supplied Name" / "Supplied Email" on the form fixed: form labels "Your name" / "Your email", zh 「您的姓名」「您的邮箱」, es and ja too (each already carried these labels). The object keeps the agent-side labels. case.view.ts:534-535, packs _sections.how_can_we_help.fields.supplied_*.label
Row 2 · Annual Revenue shows "0.00" on Contact Us platform-only. There is no default: the input's value is empty. "0.00" is the console currency widget's placeholder fallback (@objectstack/console 18.2.0 ui-components-*.js: placeholder: s?.placeholder || '0.00'), on every currency input. A per-row placeholder would hide it here, but AGENTS.md rule 4 says a bad platform default is fixed at the default, so no change. n/a
Row 3 · case number on the thank-you platform-only. The door answers { id } only; submitBehavior thank-you takes a plain title / message that the renderer prints as is (no {{record.x}} interpolation, only redirect interpolates); and submitBehavior is not translated, so a custom message would show English to a zh visitor. No change. n/a

Also in this PR:

  • Docs. In-app Service Manual (src/service/docs/crm_service_cases.md + .zh.md): new section on the public support form, the priority cap and the hotline hint. Docs site content/docs/service/cases.mdx (+ zh-Hans, zh-Hant): five fields not six, the visitor labels, the priority cap and the hint.
  • Changeset (patch).

Tests

  • New test/web-to-case-guest-priority.test.ts (5): the form declares no priority while the staff form keeps it; the door drops a posted critical (stored low, nothing escalates, no run of case_escalation_on_create, no notice to the service manager); the guest branch caps critical at high with rank 3 and nothing on the escalation path; a guest's high/medium/low are left alone; a staff critical still escalates.
  • case-sla-matrix: the anonymous-critical case now expects the cap and the High × SMB row; the no-account critical case runs as staff.
  • flow-escalation-ownerless-case: the insert-time twin is driven by an ownerless SYSTEM insert (new systemInsert helper beside systemUpdate), since a guest can no longer be born critical.
  • case-create-form-narrowing: the public form's field set no longer has priority. case-guest-branch-leftovers: header re-read for the new field list (comments only).

Ablations (each reverted on disk by an exact-anchor replace, run, restored with git checkout HEAD --, git diff HEAD empty after):

A1 drop the cap line in case.hook.ts      web-to-case-guest-priority + case-sla-matrix   2 failed | 39 passed
A2 put 'priority' back on web_to_case     web-to-case-guest-priority                     2 failed | 3 passed

Acceptance notes

  • Platform finding (for the seat to file): class (a) · reach: public door /_console/f/support with a form row options subset renders all four object options (screenshot), and POST /api/v1/forms/support/submit accepts a value outside the row subset · no per-form option filter on 18.2.0 · dedupe words: public form option subset, form field options ignored, anonymous form select options.
  • Platform finding: currency input placeholder fallback "0.00" reads as a filled value on an empty optional field (Contact Us Annual Revenue) · dedupe words: currency placeholder 0.00, empty currency field.
  • Platform finding: public form thank-you cannot name the created record: the submit answer is { id }, thank-you text has no interpolation and is not translated · dedupe words: thank-you message record number, submitBehavior translation.
  • A web case now arrives at Low. Whether a web case should start higher is a product call this PR does not make; the ruling's fallback says the case takes its default.
  • Deviation: the role file's worktree-first rule. This card thread's branch is platform-designated, so the work was done in the session's own checkout. The Claim names the branch.

Verification

pnpm verify at 486d3f5
pnpm verify = validate && typecheck && lint && hygiene && hygiene:tokens && build && test
 Test Files  252 passed (252)
      Tests  4823 passed | 1 skipped (4824)
VERIFY_EXIT=0

CI runs coverage and e2e.

The anonymous door on a dev boot (objectstack dev, 18.2.0)
before (495bff5)          POST /api/v1/forms/support/submit {priority: critical}  201
  CASE-00039 priority critical  status escalated  is_escalated true  reason "Auto-escalated: critical priority"  rank 4
subset + cap (probe)      same POST                                                201
  CASE-00041 priority high      status new        is_escalated false reason null  rank 3
after (486d3f5)           same POST                                                201
  CASE-00043 priority low       status new        is_escalated false reason null  rank 1
GET /api/v1/forms/support (zh-CN, probe): priority row served options [Low, Medium, High];
  /_console/f/support still offered: Low, Medium, High, Critical
Screenshots, signed out (project files issue-2368/)

before-support-en, before-support-zh-CN, before-support-priority-open-en, before-support-thank-you-en, before-contact-us-en, before-contact-us-zh-CN, after-support-en, after-support-zh-CN, after-support-thank-you-en, after-contact-us-en, after-contact-us-zh-CN, probe-form-option-subset-ignored-en.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAmA6c9KrsMQiv9s7ak

…o guest-chosen priority (#2368)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAmA6c9KrsMQiv9s7ak
…t priority cap in the Service Manual and the Cases page (#2368)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAmA6c9KrsMQiv9s7ak
@vercel

vercel Bot commented Oct 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Oct 29, 2026 4:54am UTC

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd CI plumbing and the verification pipeline metadata Declarative metadata — schema, security posture, UI surfaces backend Server-side behaviour — hooks, flows, actions labels Oct 29, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Verdict: ACCEPT · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-29T04:58Z · head 486d3f5
Implemented-by: card thread session session_01J8VVAmA6c9KrsMQiv9s7ak (report 6488327868 on #2368). Reviewed-by: the seat, against the 17-file diff and hotcrm origin/main 52083a5.

  • Shape:
    • Draft, base main, assignee huangyiirene. The body says Fixes #2368.
    • No governed path is touched, and no permission set, position or sharing rule changes.
    • The branch is cut from 495bff5. git merge-tree against 52083a5 (feat(nav): each persona's own work first in navigation and default views (#2367) #2377, which also edits case.view.ts) is clean.
    • The seat merged 52083a5 in locally and ran the case, web-to-case, navigation, escalation and runtime-coverage suites: 19 files, 354 passed.
  • Ruling held (guest priority at most High):
    • Measured first: an 18.2.0 public form cannot narrow a select per form. The row's options subset is served, but the renderer lists all four. Before this PR, the anonymous door stored an escalated Critical case (201).
    • So, by the ruling's fallback, priority is off web_to_case. The door's allow-list drops a posted priority, and the case takes its default.
    • The guest branch of case_validation caps a guest critical at high before anything reads it: capped, not refused, so the case is kept. That cap holds if the form's list ever changes.
    • Internal create is unchanged: staff can still pick Critical (tested).
  • Copy: the hotline hint is the form section's description. The guest labels "Your name" / "Your email" are in en, zh-CN, es-ES and ja-JP. The Service Manual (en, zh) and the site Cases page (en, zh-Hans, zh-Hant) are updated.
  • Rows 2–3 (the 0.00 currency placeholder, a case number on the thank-you page) are platform-only. The seat files them with the option-subset finding.
  • Tests:
    • pnpm verify exits 0 at 486d3f5: 252 files, 4823 passed.
    • web-to-case-guest-priority is new (5 cases).
    • Ablations A1 (cap removed) and A2 (priority back on the form) each went red and were restored.
  • Gates: CI on 486d3f5: every run is success except Build and Test, which is still running.
  • For the maintainer, not blocking: a web case now arrives at Low, the case default. Whether web cases should start higher is a product call, and the seat raises it with Irene.
  • Landing: non-governed. In this stroke the seat marks the PR ready and turns on auto-merge.

Generated by Claude Code

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

Labels

backend Server-side behaviour — hooks, flows, actions ci/cd CI plumbing and the verification pipeline documentation Improvements or additions to documentation metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

2 participants