Repository navigation
fix: polish rough edges from the 18.2.0 browser pass (#2338 items 5–13) - #2347
Conversation
… 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
Part of #2338 (item 8). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J8VVAnNyYSM5oQ2csRGcvh
Part of #2338 (item 9). 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
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
席位核对Verdict: ACCEPT ·
Generated by Claude Code |
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.
维护者速读
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
What changes
Item 5: the Sales Home AI card (dropped)
The
ai_briefingcard 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/agentsanswers[]on 18.2.0). A page component cannot be shown only where theaicapability 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 noai_briefingcomponent. 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_contactlistsmark_primaryandsend_emailwithrecord_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 readsMark as Primary Contact · Send Email · Add to favorites · Share · Delete; on a primary contact it readsSend Email · …(Mark as Primary is correctly hidden). The same header renders visible-gatedrecord_headeractions oncrm_knowledge_article(Helpful / Not Helpful) andcrm_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_timelineran each deal fromcreated_attoclose_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 thanclose_dateon every deal (stage_entry_datemoves with each stage), so the view now hasstartDateField: '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_eventgetsrelatedListColumns: the four party columns inATTENDEE_RESOLUTIONSorder, 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 readsrelatedListColumnson 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_completionsetscompleted_date = nullwhen a task leavescompletedfor another status, the same shape as the member lifecycle hook clearingresponse_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 bylast_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: nullshape. Evidence:11-top-articles-before.png,11-top-articles-after.png.Item 12: refusal copy
opportunity_lifecycle) and settled quote (quote_workflow):refuse()now passes its fourth argument,userMessage, for the first time in this repo. The diagnosticmessagekeeps 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 reasontest/converted-lead-guard.test.tsgives 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 showsuserMessage: 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).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.campaign_validationdid 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. Newtest/refusal-labels.test.tsreads them out of the lowered bodies and holds them to the objects' labels.test/refusal-envelope.test.tspinneduserMessage === messagefor every class; it now takes an optional user wording and, for the closed-deal case, pins thatuserMessagecarries 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
src/docs/crm_admin.mdlists the twelfth rule, Forecasts — Sales Manager Edit (forecast_manager_sharing, fix: a sales manager edits every forecast and deletes none (#2277) #2282/sales-manager profile:crm_forecastdeclaresallowDelete: falsebesidemodifyAllRecords: true, which objectstack now refuses at build (objectstack#25707); choose Modify All with delete, or edit without delete #2277).docs/feature-inventory.md: PRM-006 says 十二条 and names the forecast rule; CON-001 says the contract's sharing follows its account (controlled_by_parent); MKT-001 says there is no parent-campaign hierarchy.Tests
pnpm verifyon8d75a73: 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 on5556a28failed one test (dashboard-agent-global-filter, the oracle not modelling$ne: null); fixed in8d75a73, then the fullpnpm testre-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-cleanupandrefusal-envelope(item 12userMessage).Ablation: item 9 (task reopen)
The one
input.completed_date = null;line replaced on disk (anchor count 1 before,ablatedmarker count 1 after), with atraprestoringsrc/sales/objects/task.hook.tsfromHEAD:Acceptance notes
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'sNARRATIVE_FIELDSnamesnotes, whichcrm_opportunitydoes not have. The user sentence names only real fields; the set itself is untouched.src/sales/objects/_refusal.ts(one comment),src/service/views/case.view.tsandsrc/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*.mdxstill mentions the Ask the AI Assistant card as dated release history; left as is.ccb6ac6carries 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 folderissue-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