Skip to content

fix(refusals): name fields and stages by their labels when a converted lead or a closed deal's line items refuse an edit (#2338 items 14–15) - #2354

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-fv0k33
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-fv0k33

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread
Part of #2338 (items 14–15). Items 1–13 landed in #2346 and #2347; the card can close once this lands.

维护者速读

  • 改了什么:编辑已转化线索的锁定字段时,提示改用字段显示名("Cannot change Company on converted lead Ada Lovelace - Acme; only its Description and Notes can still be edited. …");已关闭商机的行项目被增删改时,提示里的阶段改用显示名(Closed Won / Closed Lost)。
  • 为什么改: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 第 14、15 项,与第 12 项同族:控制台提示里出现了 company、closed_won 这类 API 名。
  • 风险与回滚:只动两个 hook 的提示文案和测试。错误码 RECORD_LOCKED / 409 不变;给日志和 API 调用方的诊断 message 原样保留(含 API 名与尝试修改的字段),给用户看的句子走 userMessage。回滚即还原本 PR。
  • 先看:src/sales/objects/lead.hook.ts(转化线索锁的 FIELD_LABELS 表与用户句),src/revenue/objects/opportunity_line_item.hook.ts(STAGE_LABELS),test/lead-duplicate-management.test.ts(源码扫描守卫只剥离那一张标签表)。
  • 两分钟验证:pnpm vitest run test/refusal-labels.test.ts test/converted-lead-guard.test.ts test/opportunity-line-freeze.test.ts,预期全部通过;或在控制台编辑一笔 Closed Won 商机的行项目数量并保存,预期提示 "…is closed (Closed Won), so its line items can no longer be added, changed or removed."。
  • 需要你做的:无,席位落地。

Type of Change

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

What changes

Item 14: converted-lead lock (lead_automation)

  • The diagnostic message is unchanged: Cannot edit converted lead Ada Lovelace - Acme (attempted: company). Make changes on the converted records instead. test/converted-lead-guard.test.ts pins the attempted API keys on purpose, and they stay.
  • refuse() now passes a label userMessage: Cannot change Company on converted lead Ada Lovelace - Acme; only its Description and Notes can still be edited. Make changes on the converted records instead. Several fields read "Email and Lead Score".
  • The lock holds every crm_lead field outside its ALLOWED set, so the body carries a FIELD_LABELS table with one entry per held field (35). A lowered body cannot read object metadata, so the labels are written out, the same route item 12 took.
  • test/refusal-labels.test.ts reads that table out of the lowered body. It asserts that the keys are exactly the crm_lead fields outside ALLOWED and that each label equals the object's label. A field added, relabelled or dropped without the table turns it red.

Item 15: closed-deal line-item freeze (opportunity_line_item_freeze)

  • The diagnostic keeps closed (closed_won). The userMessage reads Opportunity Globex Training Package is closed (Closed Won), so its line items can no longer be added, changed or removed.
  • STAGE_LABELS is held to crm_opportunity.stage's option labels by test/refusal-labels.test.ts.
  • The approval-lock sentence on the same hook names no API value and is unchanged.

Pins extended

Tests

pnpm verify on 946467d (this branch merged with origin/main 1f1ea12): validate ✓, typecheck ✓, lint --strict ✓, hygiene ✓, token ratchet ✓, build ✓; pnpm test: Test Files 236 passed (236) · Tests 4548 passed | 1 skipped (4549), VERIFY_EXIT=0.

An earlier full run on 38a41be failed one test, the lead-duplicate-management source scan described above. It was fixed in 9ef687f and tightened in 00fff87.

Ablations (one-shot, each restored from HEAD; git diff HEAD empty and hash-object equal to the HEAD blob afterwards)
  • A1: the lead hook's userMessage argument removed. converted-lead-guard went red, ABL_EXIT=1, 1 failed of 20 (tells the user the same refusal in field labels).
  • A2: the line-item hook's userMessage argument removed. opportunity-line-freeze went red, ABL_EXIT=1, the closed-deal cases failed.
  • A3: if (input.disqualification_reason === 'duplicate') return; was injected in the lock, outside the table. The lead-duplicate-management scan went red, ABL_EXIT=1, so the strip does not widen.

Each mutation was confirmed on disk with grep -cF before the run.

Verification

Browser, on a dev server booted from this branch's built artifact (dist/objectstack.json, so the lowered hook bodies ran), 18.2.0, as admin:

  • I edited the Quantity of a line item on Globex Training Package (Closed Won) and clicked Update. The dialog banner and the toast both show Opportunity Globex Training Package is closed (Closed Won), so its line items can no longer be added, changed or removed. Screenshot: project files issue-2338-p4/15-line-item-refusal.png.
  • REST, same record: PATCH /api/v1/data/crm_opportunity_line_item/ID answered 409 with code: RECORD_LOCKED, error holding the diagnostic (closed_won) and userMessage holding (Closed Won).
  • The converted-lead site was not re-checked in the browser, because the seed data holds no converted lead. Its label sentence is pinned on the shipped (QuickJS) path and the in-process path in refusal-envelope, and the console reads userMessage the same way for every hook.

Acceptance notes

  • Same-family finding, not fixed here (the order asked for a list only): the quote line-item freeze, src/revenue/objects/quote.hook.ts:105, answers Quote QTE-… is accepted, so its line items can no longer be added, changed or removed., which shows the raw status (accepted / expired). test/quote-line-freeze.test.ts:103 pins that sentence. No other hook refusal shows an API value. I read every refuse( call under src/, and the rest name labels, record titles, counts or dates.
  • The first commit 38a41be carries the fleet bot as git author through a local override. The later commits use the container's default identity. The squash merge decides the landed author either way.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAjXujUz9ErJ5BBgVDx

objectstack-fleet Bot and others added 4 commits October 27, 2026 23:41
…d lead or a closed deal's line items refuse an edit

Part of #2338 (items 14 and 15).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAjXujUz9ErJ5BBgVDx
…ad lock's label table

Part of #2338 (item 14).

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

vercel Bot commented Oct 28, 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 28, 2026 4:25am UTC

Request Review

@github-actions github-actions Bot added 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 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Verdict: ACCEPT · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-28T04:28Z · head 946467d
Implemented-by: card thread session session_01J8VVAjXujUz9ErJ5BBgVDx (report 6460657794 on #2338) · Reviewed-by: the seat, against the 9-file diff and hotcrm origin/main 3801c17

  • Shape: draft, base main, assignee huangyiirene. The body reads Part of #2338 (items 14–15). No governed path is touched. The diff stays within claim 6456278441 plus the extension the seat approved by message: the lead-duplicate-management guard and the _refusal.ts comment. git merge-tree against 3801c17 is clean.
  • Ruling held: both items follow item 12's shape. The diagnostic message is unchanged, a label userMessage is added, and the code stays RECORD_LOCKED / 409.
    • Item 14, converted-lead lock: the user reads "Cannot change Company on converted lead …; only its Description and Notes can still be edited." Description and Notes are the lock's ALLOWED set, so that sentence is true. The diagnostic keeps (attempted: company), which converted-lead-guard pins. FIELD_LABELS covers the 35 held fields, and refusal-labels holds it to crm_lead's labels.
    • Item 15, line-item freeze: the user reads "(Closed Won)". STAGE_LABELS is held to crm_opportunity.
  • Guard extension: the duplicate-management scan strips exactly the one named FIELD_LABELS const. It asserts a single match with no nested brace and keeps a positive control on the lock sentence. Ablation A3, an injected input.disqualification_reason read outside the table, still turns it red.
  • Tests:
    • Ablations A1, A2 and A3 each went red and were restored.
    • refusal-envelope pins the user channel at both sites.
    • Browser: the line-item site shows the label in the banner and the toast. The converted-lead site was not measured in the browser, because no converted lead is seeded. It is pinned on both the shipped path and the in-process path, and the seat accepts that.
  • Gates:
    • pnpm verify exit 0 at 946467d: 236 files, 4548 passed, 1 skipped.
    • CI on 946467d: 8 of 9 check runs are success. Build and Test (22.x) is still running, and auto-merge waits for it.
  • Out of scope: the quote line-item freeze (quote.hook.ts:105) shows the raw status accepted / expired. The seat adds it 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 item 16, in the same family.
  • Deviation noted: 38a41be carries the fleet bot as git author through a local override. 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 metadata Declarative metadata — schema, security posture, UI surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants