Skip to content

fix: polish rough edges from the 18.2.0 browser pass (#2338 items 5–13) - #2347

Merged
objectstack-fleet[bot] merged 10 commits into
mainfrom
claude/project-thread-skc6v8
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 10 commits into
mainfrom
claude/project-thread-skc6v8

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread
Part of #2338 (items 5–13). Items 1–4 are another PR; item 6 needs no change (measured below), so the card stays open for items 1–4.

维护者速读

  • 改了什么:Sales Home 去掉了指向不存在面板的 AI 卡片;Deal Timeline 改为每笔交易落在成交日期上(不再出现倒着走的区间);会议的 Event Attendees 列表显示参会人姓名;已完成任务被重新打开时清空 Completed Date;侧边栏「Lead Inflow」改名为「Lead Engagement」,与报表实际统计的口径一致;Top Resolving Articles 不再列出「—」空行;成交商机、已接受/已过期报价、已激活合同的拒绝提示改用字段与状态的显示名;管理员文档补上第十二条共享规则,并更正功能清单的三行。
  • 为什么改:QA: complete browser pass of HotCRM on @objectstack/* 18.2.0 — all 161 feature-inventory rows, Setup and Studio, in six tracks #2320 浏览器巡检(18.2.0)报告的 Browser pass #2320 on 18.2.0 — HotCRM-side defects (13 items: dashboards shown to personas with no access, a dead-end case close, campaign editors who cannot edit, a toolbar action that always fails, …) — list card, split per item at dispatch #2338 第 5–13 项:这些文案或界面会误导用户,但不挡路。
  • 风险与回滚:只动元数据、一个任务 hook 分支、三个 hook 的提示文案、测试与文档。商机与报价的诊断 message 仍保留 API 名与尝试修改的字段,给用户看的句子走 userMessage;合同的提示整句换了措辞,若有外部工具匹配旧文案需改为匹配 RECORD_LOCKED。回滚即还原本 PR。
  • 先看:src/sales/objects/opportunity.hook.ts 与 src/revenue/objects/quote.hook.ts(两句话分工的拒绝),src/sales/objects/task.hook.ts(重新打开时清空),src/sales/views/opportunity.view.ts(时间线起点)。
  • 两分钟验证:pnpm vitest run test/refusal-labels.test.ts test/attendee-type-resolution.test.ts test/knowledge-deflection.test.ts,预期全部通过;或在控制台编辑一笔 Closed Won 商机的 Amount 并保存,预期提示「…is closed (Closed Won); only its Description and Next Steps can still be edited.」。
  • 需要你做的:无,席位落地。

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Documentation update

What changes

Item 5: the Sales Home AI card (dropped)

The ai_briefing card told every user to "open the assistant panel from the right edge of the page". On an install without the platform's assistant there is no such panel (GET /api/v1/ai/agents answers [] on 18.2.0). A page component cannot be shown only where the ai capability is present, so the card is dropped rather than reworded, with its four locale-pack entries. The #1002 persona guard read that card's copy; with the card gone it would read nothing, so it is replaced by a pin that Sales Home carries no ai_briefing component. Evidence: 05-sales-home-after.png.

Item 6: contact header actions (no change; premise does not hold on 18.2.0)

Measured on this branch's base with the dev server: GET /api/v1/meta/object/crm_contact lists mark_primary and send_email with record_header, and the synthesized record header renders both. They sit in the header's ⋯ menu, after the three inline activity actions. On a non-primary, opted-in contact (created for the probe) the ⋯ menu reads Mark as Primary Contact · Send Email · Add to favorites · Share · Delete; on a primary contact it reads Send Email · … (Mark as Primary is correctly hidden). The same header renders visible-gated record_header actions on crm_knowledge_article (Helpful / Not Helpful) and crm_task (Complete). So neither a contact page nor a platform card is needed. The tester's ⋯ menu listed "Add to favorites / Open in new tab / Copy link", which does not match the 18.2.0 menu either; that difference is NOT MEASURED here. Evidence: 06-contact-header-menu-measured.png.

Item 7: Deal Timeline

deal_timeline ran each deal from created_at to close_date, and nothing orders those two: a backdated close, a deal entered after it closed, or an overdue open deal drew a backwards range ("10/27/2026 → 9/7/2026" on the seeded book). No stored date is earlier than close_date on every deal (stage_entry_date moves with each stage), so the view now has startDateField: 'close_date' and no end field: one mark per deal on its Close Date, which is required and NOT NULL. Docs (sales/opportunities, three languages) follow. Evidence: 07-deal-timeline-before.png, 07-deal-timeline-after.png.

Item 8: Event Attendees related list

crm_event_attendee.crm_event gets relatedListColumns: the four party columns in ATTENDEE_RESOLUTIONS order, then type, response and organizer. The panel drops a column that is empty on every fetched row, so a meeting with a user, an external guest and a contact shows Contact / User / External Attendee / Attendee Type / Response / Organizer (Lead dropped). Mechanism assumption 1 holds: the console reads relatedListColumns on the child's lookup. Evidence: 08-event-attendees-before.png (Attendee Type / Response / Organizer only), 08-event-attendees-after.png.

Item 9: a reopened task keeps its Completed Date

task_completion sets completed_date = null when a task leaves completed for another status, the same shape as the member lifecycle hook clearing response_date. The key is hook-written, so the readonly strip leaves it alone; measured through REST on the dev server: completed → 2026-10-27T19:43:31Z, back to in_progress → null.

Item 10: "Lead Inflow"

The report (lead_inflow_by_month_source) is already titled "Lead Engagement by Month × Source" and counts contacted leads by last_contacted_date. The nav label was the mismatch, so it becomes Lead Engagement (zh-CN 线索跟进, es-ES Interacción con Prospectos, ja-JP リード対応, each taken from the report's own pack label). The report id stays. Docs naming the entry follow: getting-started/quick-tour, analytics/index, analytics/reports, marketing/index (three languages each); the marketing page now says what the report counts. Evidence: 10-reports-nav-after.png.

Item 11: Top Resolving Articles

The widget filter gains resolved_by_article: { $ne: null }. Before, closed cases with no article formed a blank bucket drawn as "—" with 0. Probed on the analytics door first: the same query with the key returns only the article rows. test/dashboard-agent-global-filter.test.ts's truth oracle throws on any filter shape it does not model, so it learns the $ne: null shape. Evidence: 11-top-articles-before.png, 11-top-articles-after.png.

Item 12: refusal copy

  • Closed deal (opportunity_lifecycle) and settled quote (quote_workflow): refuse() now passes its fourth argument, userMessage, for the first time in this repo. The diagnostic message keeps API names and the attempted keys, because nothing gets a turn after this throw and API callers and logs need to know which key tripped the freeze (the reason test/converted-lead-guard.test.ts gives for naming the field). The user sentence names the stage, status and fields by label: "Opportunity Globex Training Package is closed (Closed Won); only its Description and Next Steps can still be edited." and "Quote QTE-0002 - … is Accepted; only its Internal Notes can still be edited." The console shows userMessage: measured in the Edit dialog, both the banner and the toast carry the label sentence (12-closed-deal-refusal.png, 12-accepted-quote-refusal.png).
  • Activated contract (contract_validation): one sentence, so the message itself changes: "An activated contract's End Date cannot move earlier (2027-10-26 → 2027-10-10). Use a termination/amendment workflow instead."
  • RECORD_LOCKED / 409 is unchanged on all three.
  • Labels are written out in the bodies (a lowered body cannot read object metadata), as campaign_validation did for Browser pass #2058 — HotCRM-side defects (18 items: accepted quote still takes lines, half-converted leads, agents losing their cases, unreachable Sales Home, …) — list card, split per item at dispatch #2060 item 12. New test/refusal-labels.test.ts reads them out of the lowered bodies and holds them to the objects' labels.
  • test/refusal-envelope.test.ts pinned userMessage === message for every class; it now takes an optional user wording and, for the closed-deal case, pins that userMessage carries the label sentence and differs from the diagnostic. src/sales/objects/_refusal.ts's note that "no site needs that today" is updated.

Item 13: docs rows

Tests

pnpm verify on 8d75a73: validate ✓ (3737 ms), typecheck ✓, lint --strict ✓ (19 advisory suggestions, no warnings or errors), hygiene ✓, token ratchet ✓, build ✓; pnpm test: Test Files 231 passed (231) · Tests 4472 passed | 1 skipped (4473). The first verify run on 5556a28 failed one test (dashboard-agent-global-filter, the oracle not modelling $ne: null); fixed in 8d75a73, then the full pnpm test re-ran green on that head.

New or changed pins: refusal-labels (new, 3), attendee-type-resolution (+1, item 8), hooks-runtime-service (+2 for item 9, contract wording), knowledge-deflection (item 11), opportunity-creation-date (item 7), metadata-references (item 5 replaces the #1002 block), freeze-guard-reference-cleanup and refusal-envelope (item 12 userMessage).

Ablation: item 9 (task reopen)

The one input.completed_date = null; line replaced on disk (anchor count 1 before, ablated marker count 1 after), with a trap restoring src/sales/objects/task.hook.ts from HEAD:

ABL_EXIT=1
× clears completed_date when a completed task is reopened (#2338 item 9)
Tests  1 failed | 7 passed | 112 skipped (120)
RESTORED (hash-object equals HEAD blob), git diff HEAD empty

Acceptance notes

  • Same family as item 12, not changed here: lead_automation's converted-lead refusal ("Cannot edit converted lead … (attempted: company)") and the closed-deal line-item freeze ("…is closed (closed_won), so its line items can no longer be…") also show API names or values to users. The lead message's attempted list is pinned on purpose (test/converted-lead-guard.test.ts, "names the offending field, because no second error follows it"), so the same two-sentence split would fit it. Reported to the seat for Browser pass #2320 on 18.2.0 — HotCRM-side defects (13 items: dashboards shown to personas with no access, a dead-end case close, campaign editors who cannot edit, a toolbar action that always fails, …) — list card, split per item at dispatch #2338's family.
  • opportunity_lifecycle's NARRATIVE_FIELDS names notes, which crm_opportunity does not have. The user sentence names only real fields; the set itself is untouched.
  • File surface beyond the claim's wording, stated here: the four locale packs (item 5 drops the card's entries; item 10's nav label), the docs naming the renamed or redrawn surfaces (items 7 and 10), src/sales/objects/_refusal.ts (one comment), src/service/views/case.view.ts and src/sales/objects/opportunity.object.ts (one comment each that cited the old timeline), test/dashboard-agent-global-filter.test.ts (item 11's oracle).
  • whats-new*.mdx still mentions the Ask the AI Assistant card as dated release history; left as is.
  • Commit ccb6ac6 carries a model-named co-author trailer the harness supplied; later commits use a model-free one. History is not rewritten.

Verification

Browser screenshots (desktop, admin, dev server on this branch with @objectstack/* 18.2.0) are in the project files folder issue-2338-p3/: 05-sales-home-after, 06-contact-header-menu-measured, 07-deal-timeline-before/after, 08-event-attendees-before/after, 10-reports-nav-after, 11-top-articles-before/after, 12-closed-deal-refusal, 12-accepted-quote-refusal.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh

claude added 10 commits October 27, 2026 19:38
… may not have

Part of #2338 (item 5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh
…, so no bar runs backwards

Part of #2338 (item 7).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh
…ts report counts

Part of #2338 (item 10).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh
… Resolving Articles

Part of #2338 (item 11).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh
… a settled quote or an activated contract refuses an edit

Part of #2338 (item 12).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh
…gn inventory rows; changeset

Part of #2338 (item 13).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh
…value" filter

Part of #2338 (item 11).

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

vercel Bot commented Oct 27, 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 27, 2026 11:22pm 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 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Verdict: ACCEPT · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-27T23:28Z · head 8d75a73
Implemented-by: card thread session session_01J8VVAnNyYSM5oQ2csRGcvh (report 6456063458 on #2338) · Reviewed-by: the seat, against the 45-file diff and hotcrm origin/main a31d91a

  • Shape:

  • Ruling held, item by item:

    • 5: the ai_briefing card is dropped along with its pack entries, and an absence pin replaces the copy guard. No capability check was invented; the missing page-level capability gate is named.
    • 6: the premise is false on 18.2.0. The synthesized contact header already renders Mark as Primary and Send Email in its overflow menu, as measured. Nothing changed and no platform card is needed.
    • 7: Deal Timeline marks each deal on close_date, which is required and NOT NULL, with no end field. No bar can run backwards. No stored date precedes close_date on every deal, and the reasoning is recorded.
    • 8: relatedListColumns on crm_event_attendee.crm_event names the party, type, response and organizer.
    • 9: task_completion clears completed_date when a task leaves completed. An ablation turns that test red.
    • 10: the nav label is now Lead Engagement in four packs and the docs. The report id is unchanged.
    • 11: the widget filter adds resolved_by_article: { $ne: null }, so the "—" row is gone.
    • 12: RECORD_LOCKED / 409 is unchanged. The closed-deal and settled-quote refusals keep the diagnostic message and add a label userMessage, which is what the console shows (measured in the Edit dialog). The contract refusal names End Date by its label. test/refusal-labels.test.ts reads the labels out of the lowered bodies, and refusal-envelope pins the user channel separately.
    • 13: crm_admin.md gains forecast_manager_sharing, and the inventory rows PRM-006 (twelve), CON-001 and MKT-001 are corrected.
  • Gates:

    • Locally, pnpm test exit 0 at 8d75a73 (231/231 files, 4472 passed, 1 skipped). The rest of the verify chain passed at 5556a28; since then only one test oracle has changed.
    • CI on 8d75a73: 9 of 10 check runs are success. Build and Test (22.x) is still running, and the merge queue re-tests the merged result before the merge.
  • Out of scope, carried by the seat: two refusals in the same family as item 12 still show API values. They are added to Browser pass #2320 on 18.2.0 — HotCRM-side defects (13 items: dashboards shown to personas with no access, a dead-end case close, campaign editors who cannot edit, a toolbar action that always fails, …) — list card, split per item at dispatch #2338 as items 14 and 15 for a later order:

    • the converted-lead freeze lists the attempted API keys (lead.hook.ts:447);
    • the line-item freeze names closed_won.

    The notes key in NARRATIVE_FIELDS that is never used, and the page-level AI capability gate, are noted only.

  • Deviation noted: commit ccb6ac6 carries a model-named trailer, and history is not rewritten.

  • Landing: non-governed. The seat flips the PR to ready and arms auto-merge in this stroke.


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

Development

Successfully merging this pull request may close these issues.

2 participants