Skip to content

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

Closed

Description

@objectstack-fleet

Filing gate: ① product defects with reach measured. These are the HotCRM-side findings of the #2320 browser pass, run by six cloud testers on main with @objectstack/* 18.2.0. The repo:hotcrm seat re-read each landing site on origin/main ea567f2. Filed by the repo:hotcrm seat (huangyiirene, session session_01J8VVAnPkgK6CWrJa6t7Fk5).

Who acts: the repo:hotcrm seat. This is a list card, like #2060 and #2073. Each item is split out or dispatched on its own when it is picked up. The items are folded here because the fleet write budget is 40 writes per hour.

Evidence: every item names its track and finding id. The reports are comments on #2320:

Track Report comment
Sales 6443461344
Setup 6442758420
Studio 6443071470
Service 6444034183
Marketing 6444160853
Revenue 6444188882 and 6450345451

The screenshots are in project files qa-2320/<track>/, and on each track's branch named in its claim.

Not here:

The items

p2: a user is stopped, or sees the wrong thing

  1. Dashboards are offered to personas who cannot read the data behind them (Setup D1, D2; Marketing D12).
    • A user holding marketing_user sees Service Overview. All 9 tiles say "You don't have permission to view the data behind this widget", and POST /api/v1/analytics/dataset/query returns 403. The set's own description says it "see[s] no cases".
    • The same user's Sales Performance has 2 no-access tiles; these are likely the crm_forecast ones, since the set has no forecast read.
    • Sales Activity shows that user zeros and "No data yet" beside "Open Deals 10".
    • Landing:
      • src/service/index.ts:209 nav_service_dashboard (visible: NOT_A_SALES_REP only);
      • src/sales/apps/crm.app.ts:262 nav_sales_dashboard (OPPORTUNITY_AUDIENCE), or the forecast tiles' audience;
      • src/sales/apps/crm.app.ts:379 nav_activity_dashboard (no visible).
    • Gate them on read access, the way Cases and My Cases are already hidden for this user. Per the [Decision] A sales rep opens the Executive Overview by URL although the sidebar hides it: is the nav visible predicate an access boundary (needs a server-side gate) or only tidying #2287 ruling, nav visible is tidying, not a boundary, so this is about not offering a page that cannot work.
  2. Edit Case offers Status "Closed" but has no Resolution field, so the save is a dead end (Service D3).
    • The save is refused with "Resolution is required when closing a case", and there is nothing to fill in. The Resolution section is hidden on the record while it is empty, so the only way out is Cancel, then Close Case.
    • Landing: src/service/views/case.view.ts form sections[0].fields. The same form is the create dialog, so make Resolution visible when the status is resolved or closed, rather than always on.
    • Evidence: svc-45-sm-close-without-resolution.png.
  3. Marketing Manager and Marketing Director cannot edit live campaigns (Marketing D3).
    • A user holding only marketing_manager opens CPG-0003 (planning). There is no Edit or Enroll, and a PATCH returns 403.
    • Both positions (src/sales/sharing/positions.ts, around lines 94–101) declare no permissionSets. The object-level gate therefore refuses before the campaign_leadership_* edit share can apply.
    • Their descriptions and inventory MKT-008 promise "edits every live campaign, whoever owns it".
    • Evidence: mkt-12-mktmgr-*.
  4. "Update Stage" in the opportunity list toolbar fails on every selection (Sales D6).
    • With 2 rows ticked it says "This action runs on a single record — select exactly one row". With none ticked, the dialog opens and targets nothing. Only the bulk-bar copy in Open Deals works.
    • Landing: src/sales/actions/opportunity.actions.ts:202 locations: ['list_toolbar'] on mass_update_stage. Measure whether dropping the toolbar location is enough, since the views' bulkActionDefs already carry the action.
    • Evidence: opp-13-mass-stage-two-open.png, opp-13-mass-stage-none.png.

p3: misleading, but not blocking

  1. Sales Home tells every user to open an assistant panel this install does not have (Sales D2 = Setup D9).
    • The card says "Open the assistant panel from the right edge of the page…", but there is no such control. GET /api/v1/ai/agents returns [], and skills return 501.
    • Landing: src/sales/pages/home.page.ts:295 ai_briefing. Reword it, or drop it until the platform can show a component only when the ai capability is present; if so, that is a platform card.
  2. The contact record page offers neither Mark as Primary nor Send Email (Sales D3).
    • Both are declared with locations: ['record_header', 'list_item'] in src/sales/actions/contact.actions.ts, but they appear only in the row menu. crm_contact has no authored page, and the synthesized header shows only the activity actions.
    • Measure first: whether the console's synthesized header is meant to take record_header actions (then it is a platform card), or whether the app needs a contact page.
    • Evidence: con-05-nonprimary-header.png.
  3. Deal Timeline draws every deal from created_at, so closed deals show backwards bars (Sales D4).
    • A deal closed in September reads "10/27/2026 → 9/7/2026".
    • Landing: src/sales/views/opportunity.view.ts:175.
    • Evidence: opp-01-04-deal-timeline.png.
  4. The Event Attendees related list shows no attendee names (Marketing D5).
    • The list has three columns (Attendee Type, Response, Organizer), so it cannot tell who is attending.
    • The related list reads highlightFields (src/sales/objects/event_attendee.object.ts:184), not the curated view.
    • Evidence: act-13-event-record-tab-related2.
  5. A reopened task keeps its Completed Date (Marketing D6).
    • Completed, then back to In Progress: completed_date stays, and the field is read-only, so the user cannot clear it.
    • Landing: src/sales/objects/task.hook.ts task_completion. Clear the stamp on leaving completed, as the member lifecycle hook does.
  6. Sidebar "Lead Inflow" opens a report about lead engagement (Marketing D8).
    • It counts contacted leads by last_contacted_date. Logging a call on a May lead moved that lead to October.
    • Landing: src/sales/apps/crm.app.ts:451 (the label) or the report's name and definition.
  7. "Top Resolving Articles" lists a "—" row with 0 (Service D4 = Marketing D9).
    • Closed cases with no article form a blank bucket.
    • Landing: src/service/dashboards/service.dashboard.ts:293. Exclude a null resolved_article in the widget filter.
  8. Refusal copy shows API names to users (Revenue D3).
    • Examples:
      • "…is accepted; only internal_notes may be edited. Attempted: discount." (src/revenue/objects/quote.hook.ts:224)
      • "Cannot shrink end_date (…) after activation." (src/sales/objects/contract.hook.ts:93)
      • The same shape on closed deals (src/sales/objects/opportunity.hook.ts:305).
    • Use the field labels the user sees. This overlaps theme 5 of the UX summary, so merge them if the maintainer adopts that theme.
  9. Three docs rows are wrong (Setup D3, Revenue D5, Marketing D7).
    • Inventory PRM-006 says 「十一条」 and src/docs/crm_admin.md lists eleven rules. Twelve ship; forecast_manager_sharing (fix: a sales manager edits every forecast and deletes none (#2277) #2282) is missing.
    • Inventory CON-001 says 私有共享, but contract.object.ts:20 is controlled_by_parent.
    • Inventory MKT-001 promises 父活动层级, but campaign.object.ts has no parent_campaign.

Dedupe

I searched hotcrm issues with these queries:

  • "Sales Home AI assistant card panel does not exist"
  • "marketing user dashboard tiles no access Service Overview navigation visible"
  • "marketing manager director position permission set edit campaign"
  • "case edit form status closed resolution field missing; update stage toolbar single record; contact record header send email mark primary"
  • "event attendee related list names; reopened task completed date; deal timeline created_at; top resolving articles dash row; lead inflow report label"

All the hits are closed: #1002, #2295, #2250, #2287, #2244, #510, #2060, #970, #2127, #2073, #2288, #2291, #2238, #2126 and #2158. None reports these items as they stand on 18.2.0. The one open hit, #2296, is the KPI compare card, which is unrelated.

Grade: priority:p2, from the highest item. Items 5–13 are p3 when they are split out.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · #2338 items 1–4 (the p2 section) · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-27T18:58Z

    This order takes the slot that #2297 freed (PR #2339 is accepted and auto-merge is armed). Irene asked for dispatch to continue at concurrency 5 (「继续派发」, 「4小时之后可以继续」). This is a list card. This order dispatches only items 1–4; items 5–13 stay on the card for later orders.

    Container & model: M · mode:thread · model TIER_DEFAULT · effort high (the thread runs at the project default).
    Role file: objectstack-ai/objectstack .claude/agents/os-dev.md (read it in full). hotcrm AGENTS.md binds. Re-measure every file:line against origin/main (ea567f2 or later).
    Writes: the PR goes only through the relay op pr_create (draft, base main, assignees: [huangyiirene]). Comments go only through scripts/pm/post-stamped.mjs. ⛔ No GitHub MCP write, and ⛔ no issue filing; out-of-scope findings go in the report. The write budget is tight, so make two writes: the PR and the report.
    Report: the os-dev-report JSON, as a comment on this card and as the final message to the seat thread. ⛔ Start editing only after the newest Claim: on this card names your branch. Push work in progress at each milestone, so a stop loses nothing.

    Ruling (not to be re-decided)

    • One PR covers all four items. Its first line is Part of #2338 (items 1–4). The PR body has one section per item, with its evidence.
    • Item 1 (dashboards offered to personas who cannot read them):
    • Item 2 (Edit Case → Closed with no Resolution field):
      • Add Resolution to the case form, visible when the status is resolved or closed.
      • ⛔ Do not make it always visible, because the same form is the create dialog.
    • Item 3 (Marketing Manager and Director cannot edit live campaigns):
      • Give both positions a permission set whose object grants let the campaign_leadership_* edit share apply.
      • Measure first: say which set they need (an existing one or a new one) and why. Name every grant that changes for them.
    • Item 4 ("Update Stage" in the list toolbar fails): make the toolbar copy work on the selection, or remove it in favour of the bulk bar. Measure which one the console supports on 18.2.0.

    PM mechanism assumptions (measure first, on the installed 18.2.0)

    1. A nav visible predicate can read object read access (current_user.can(...) or the repo's existing audience predicates) for a user who holds only marketing_user.
    2. A form field's visibleWhen on status works on the Edit dialog and keeps the field off the New dialog.
    3. A list_toolbar action with a selection runs on the whole selection, or the console offers no such mode.

    A refusal is a platform card, not a workaround. Stop and report it with evidence.

    Suggested route (optional)

    • Browser re-check per item, using the persona from the finding, with screenshots. Use a marketing_user created in Setup for items 1 and 3, and the service agent for item 2.
    • Tests: an audience pin for the three nav entries, the case form's visibleWhen, and the positions' sets.
    • A changeset.

    Serial constraints:


    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 Oct 27, 2026
  3. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01J8VVAnPkgK6CWrJa6t7Fk5
    Account: huangyiirene
    Branch: claude/project-thread-6amvua
    Worktree: hotcrm-issue-2338 (card thread; branch is platform-designated, the measured platform exception to claude/issue-N-slug; implementing session session_01J8VVAk32RuJpqjMZ6Lqc7k)
    Domain: repo:hotcrm (single-lane repo, no domain:*)
    Seat: repo:hotcrm#1
    File surface: items 1–4 only: src/service/index.ts and src/sales/apps/crm.app.ts nav audiences, forecast tile audiences in src/sales/dashboards/sales.dashboard.ts if that is the route, src/service/views/case.view.ts (Resolution visibleWhen), src/sales/sharing/positions.ts and a permission set under src/sales/profiles/ (item 3), src/sales/actions/opportunity.actions.ts mass_update_stage locations, the four locale packs if a label is added, docs naming these surfaces (en, zh-Hans, zh-Hant), tests under test/, .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (sister repo, hand-derived), effort: the order asked high; the thread runs at the project default, which only the maintainer sets
    Clause-②: no
    Metadata-protocol: none
    Responsibility: hotcrm app metadata (nav audiences, form visibleWhen, position sets, action locations); a refusal is a platform card
    Thread-read: 6450948994
    Premise: holds — re-read at claim on hotcrm origin/main ea567f2 (order comment 6450948994)
    Serial constraints cleared: none; #2296 edits KPI compareTo (whoever lands second merges origin/main), #2334 opportunity approval, #2336 enroll flows, #2276 registration

  4. added a commit that references this issue on Oct 27, 2026
  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · #2338 items 5–13 (the p3 section) · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-27T19:27Z

    This order takes the slot freed by #2296 (PR #2340). Items 1–4 are already dispatched under order 6450948994 (claim 6451041709, branch claude/project-thread-6amvua). This order covers items 5–13 only.

    Container & model: M · mode:thread · model TIER_DEFAULT · effort high (the thread runs at the project default).
    Role file: objectstack-ai/objectstack .claude/agents/os-dev.md. Read it in full. hotcrm AGENTS.md binds. Re-measure every file:line against origin/main (60d745b or later).
    Writes:

    • The PR goes only through the relay op pr_create: draft, base main, assignees: [huangyiirene].
    • Comments go only through scripts/pm/post-stamped.mjs.
    • ⛔ No GitHub MCP write. ⛔ No issue filing: out-of-scope findings go in the report.
    • The write budget is tight, so make two writes: the PR and the report.

    Report: the os-dev-report JSON, posted as a comment on this card and sent as the final message to the seat thread. ⛔ Start editing only after the newest Claim: on this card names your branch for items 5–13. Push work in progress at each milestone, so a stop loses nothing.

    Ruling (not to be re-decided)

    • One PR, with first line Part of #2338 (items 5–13). The body has one section per item.
    • Item 5 (AI card): reword the ai_briefing card so it names nothing the user cannot find, or drop it. Say which you chose. ⛔ Do not add a capability check the platform does not offer; if one is needed, name it as an out-of-scope finding.
    • Item 6 (contact header actions): measure first. If the synthesized record header ignores record_header actions by design, add a minimal contact record page with Mark as Primary and Send Email, as the lead and opportunity pages do. If the console should show them and does not, stop on that item and report it as a platform finding.
    • Item 7 (Deal Timeline): bars must not run backwards. Choose a start field that exists on every deal, and say which one.
    • Items 8–11: as the card's landing describes them.
      • Event attendee related-list columns.
      • Clear completed_date on leaving completed.
      • "Lead Inflow": the nav label or the report, whichever makes the name match what it counts.
      • The null resolved_article filter on the "Top Resolving Articles" widget.
    • Item 12 (refusal copy): the three hooks name fields by their labels. Keep the RECORD_LOCKED / 409 code. Update any test that pins the old text.
    • Item 13: correct the three docs rows (PRM-006 count and the crm_admin.md row, CON-001, MKT-001).

    PM mechanism assumptions (measure first, on the installed 18.2.0)

    1. relatedListColumns on the crm_event lookup, or highlightFields on crm_event_attendee, changes the Event Attendees related list.
    2. An authored contact record page can carry record_header actions (item 6).

    A refusal is a platform card, not a workaround. Stop on that item and report it with evidence.

    Suggested route (optional)

    • Browser re-check per item, using the persona in the finding, with screenshots.
    • Tests per item where a pin is cheap: task completion, the widget filter, the refusal texts.
    • Changeset.

    Serial constraints:


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01J8VVAnPkgK6CWrJa6t7Fk5
    Account: huangyiirene
    Branch: claude/project-thread-skc6v8
    Worktree: hotcrm-issue-2338 (card thread; branch is platform-designated, the measured platform exception to claude/issue-N-slug; implementing session session_01J8VVAnNyYSM5oQ2csRGcvh)
    Domain: repo:hotcrm (single-lane repo, no domain:*)
    Seat: repo:hotcrm#1
    File surface: items 5–13 only: the ai_briefing card, a contact record page (item 6), the Deal Timeline in opportunity.view.ts, event-attendee related-list columns, task completed_date hook, the Lead Inflow nav/report, the Top Resolving Articles widget filter, refusal copy in the three hooks plus the tests that pin it, the three docs rows, one changeset (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (sister repo, hand-derived), effort: the order asked high; the thread runs at the project default, which only the maintainer sets
    Clause-②: no
    Metadata-protocol: none
    Responsibility: #2338 items 5–13 (the p3 section) as ruled in the order; items 1–4 stay with claim 6451041709
    Thread-read: 6451534867
    Premise: holds — re-read at claim on hotcrm origin/main bbd375b (order comment 6451534867)
    Serial constraints cleared: items 1–4 (branch claude/project-thread-6amvua) share opportunity.view.ts; #2334 shares opportunity.hook.ts/quote.hook.ts. Whichever lands second merges origin/main

  7. 8 remaining items

  8. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Items added · repo:hotcrm seat 1 · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-27T23:3xZ

    PR #2347's report (6456063458) found two more refusals in item 12's family that show users an API value. They join this card as items 14 and 15, p3, for a later order. Each follows item 12's shape: keep the diagnostic message, add a label userMessage, and keep the code.

    # Reach Evidence Target
    14 Editing a converted lead's Company answers 409 Cannot edit converted lead Ada Lovelace - Acme (attempted: company), and the console toast shows the API key. src/sales/objects/lead.hook.ts:447. test/converted-lead-guard.test.ts pins the attempted list on purpose. The user sentence names the fields by their labels. The diagnostic keeps the API keys.
    15 Editing a line item of a closed deal answers Opportunity X is closed (closed_won), so its line items can no longer be added, changed or removed. test/opportunity-line-freeze.test.ts:76 pins that sentence. The user sentence names the stage label (Closed Won).

    Status of this card: items 1–4 are PR #2346 and items 5–13 are PR #2347. Both are ACCEPTed with auto-merge armed. The card stays open for items 14 and 15.


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · #2338 items 14–15 · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-27T23:34Z

    This order takes a slot freed by #2338 items 1–13: PR #2346 has landed, and PR #2347 has landed too. It covers items 14 and 15 only, as added in comment 6456156202.

    Container & model: M · mode:thread · model TIER_DEFAULT · effort high (the thread runs at the project default).
    Role file: objectstack-ai/objectstack .claude/agents/os-dev.md. Read it in full. hotcrm AGENTS.md binds. Re-measure every file:line against origin/main (52a6fb2 or later).
    Writes:

    • The PR goes only through the relay op pr_create: draft, base main, assignees: [huangyiirene].
    • Comments go only through scripts/pm/post-stamped.mjs.
    • ⛔ No GitHub MCP write. ⛔ No issue filing; out-of-scope findings go in the report.
    • The write budget is tight, so make two writes: the PR and the report.

    Report: the os-dev-report JSON, posted as a comment on this card and sent as the final message to the seat thread. ⛔ Start editing only after the newest Claim: on this card names your branch for items 14–15. Push work in progress at each milestone, so a stop loses nothing.

    Ruling (not to be re-decided)

    • One PR. Its first line is Part of #2338 (items 14–15).
    • Follow item 12's landed shape, from PR fix: polish rough edges from the 18.2.0 browser pass (#2338 items 5–13) #2347: the refusal keeps its diagnostic message, which pinned tests may read, and passes a label userMessage. The code and status stay unchanged.
      • Item 14 is lead.hook.ts, the converted-lead freeze. The user sentence names the attempted fields by their labels, and the diagnostic keeps the API keys that test/converted-lead-guard.test.ts pins.
      • Item 15 is the closed-deal line-item freeze. The user sentence names the stage by its label (Closed Won / Closed Lost).
    • Extend test/refusal-labels.test.ts and refusal-envelope's userWording pins to cover both sites.
    • Grep the other hooks for a refusal that shows an API value to users. If any exist, list them in the report; do not fix them here.

    PM mechanism assumptions

    1. The console shows userMessage in the toast and the Edit dialog for both sites. Item 12 measured this for its sites; re-check one of these sites in the browser.

    Serial constraints: none known. #2349 edits approval flows, not these hooks.


    Generated by Claude Code

  10. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01J8VVAnPkgK6CWrJa6t7Fk5
    Account: huangyiirene
    Branch: claude/project-thread-fv0k33
    Worktree: hotcrm-issue-2338 (card thread; branch is platform-designated, the measured platform exception to claude/issue-N-slug; implementing session session_01J8VVAjXujUz9ErJ5BBgVDx)
    Domain: repo:hotcrm (single-lane repo, no domain:*)
    Seat: repo:hotcrm#1
    File surface: items 14–15 only: src/sales/objects/lead.hook.ts (converted-lead freeze), the opportunity line-item freeze hook, test/refusal-labels.test.ts, test/refusal-envelope.test.ts, test/converted-lead-guard.test.ts and test/opportunity-line-freeze.test.ts only where a pin needs it, one changeset (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (sister repo, hand-derived), effort: the order asked high; the thread runs at the project default, which only the maintainer sets
    Clause-②: no
    Metadata-protocol: none
    Responsibility: #2338 items 14–15 (label userMessage on two refusals) as ruled in the order; items 1–13 landed in #2346/#2347
    Thread-read: 6456235199
    Premise: holds — re-read at claim on hotcrm origin/main b8332aa (order comment 6456235199)
    Serial constraints cleared: none known

  11. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed, items 1–13 · repo:hotcrm seat 1 · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-28T00:0xZ

    Reading Probe Result
    PR #2346 (items 1–4) GET /pulls/2346 Merged 2026-10-27T23:27Z as b65c403, from head 9568b7c3cb, which is the head the seat ACCEPTed (6456097776).
    PR #2347 (items 5–13) GET /pulls/2347 Merged 2026-10-27T23:30Z as 52a6fb2, from head 8d75a7314a, which is the head the seat ACCEPTed (6456144351).
    main CI GET /commits/{b65c403,52a6fb2}/check-runs All 6 runs on each commit are success.

    State: items 1–13 are on main. Items 14 and 15 are in flight under claim 6456278441 (branch claude/project-thread-fv0k33), so this card keeps pm:dispatched until they land. The platform finding from item 1, that a dashboard tile has no read gate, is objectstack#26971.


    Generated by Claude Code

  12. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 2338,
     "status": "done",
     "branch": "claude/project-thread-fv0k33",
     "pr": "https://github.com/objectstack-ai/hotcrm/pull/2354",
     "session": "session_01J8VVAjXujUz9ErJ5BBgVDx",
     "premise_still_valid": true,
     "summary": "Items 14-15 in one draft PR (Part of #2338 items 14-15), item 12's shape. Item 14: lead_automation's converted-lead lock keeps its diagnostic message (attempted API keys, pinned by converted-lead-guard) and 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.'; the labels ride a FIELD_LABELS table with one entry per held field (35), held to crm_lead by refusal-labels (keys = fields outside ALLOWED, labels = object labels). Item 15: opportunity_line_item_freeze keeps '(closed_won)' in the diagnostic and gives the user '(Closed Won)' via STAGE_LABELS. RECORD_LOCKED/409 unchanged on both. refusal-envelope gains both sites on the shipped and in-process paths with userWording; opportunity-line-freeze pins userMessage on every refusal it checks, the REST DELETE envelope included. Surface extended by the seat (00:17Z): test/lead-duplicate-management.test.ts source scan strips exactly the one FIELD_LABELS const (no nested brace, asserted to match exactly once, positive control kept), and the _refusal.ts note (two sites -> four). Paused 00:19Z-04:06Z at Irene's word; merged origin/main 1f1ea12 on resume. Card assignee untouched.",
     "tests": "pnpm verify on 946467d (branch + origin/main 1f1ea12): validate, typecheck, lint --strict, hygiene, hygiene:tokens, build all green; pnpm test Test Files 236 passed (236), Tests 4548 passed | 1 skipped (4549), VERIFY_EXIT=0 captured by redirect-then-$?. Earlier full run on 38a41be: 1 failed (lead-duplicate-management source scan, 'not to contain disqualification_reason'), fixed in 9ef687f and tightened in 00fff87. Ablations (one-shot, trap restore via git checkout HEAD -- ABS path, git diff HEAD empty afterwards, hash-object equal to HEAD blob checked separately after the script's own HEADBLOB read came back empty from a set -u bug): A1 lead userMessage arg removed -> converted-lead-guard ABL_EXIT=1, 1 failed of 20; A2 line-item userMessage arg removed -> opportunity-line-freeze ABL_EXIT=1, closed-deal cases failed; A3 injected input.disqualification_reason read outside the table -> lead-duplicate-management ABL_EXIT=1 (strip does not widen). Browser: dev server booted from this branch's dist/objectstack.json (lowered bodies), admin, edited Quantity on a Globex Training Package (Closed Won) line, Update -> dialog banner and toast both show the '(Closed Won)' sentence (project files issue-2338-p4/15-line-item-refusal.png); REST PATCH on the same line -> 409 RECORD_LOCKED, error = diagnostic (closed_won), userMessage = (Closed Won) (issue-2338-p4/rest-line.txt). Converted-lead site NOT MEASURED in the browser: reason, the seed holds no converted lead and the conversion flow is approval-gated; covered on the shipped QuickJS path and in-process.",
     "gates": "hotcrm has no dispatch-gates.mjs; local set hand-derived from package.json verify chain, all in the single pnpm verify on 946467d, VERIFY_EXIT=0. PR #2354 check-runs on 946467d at report time: Vercel Preview Comments success, Label Pull Request success, link-check success, Check Changeset success; Build and Test (22.x), Quality Checks, Playwright, Analyze Code (javascript) in_progress.",
     "dogfood_battery": "n/a - no ! in the PR title and Clause-2: no",
     "mcp_calls": "2 - mcp__github__issue_read get_comments x1 on #2338 (before reading the order's no-MCP-write rule; read only), plus 0 MCP writes. Reads after that via gh api. Non-GitHub MCP: hearthbot message_thread to the seat, checklist updates.",
     "api_writes": "2 - fleet-write relay pr_create objectstack-ai/hotcrm#2354 (draft, assignee huangyiirene; read-back identical, 7569 bytes) and this os-dev-report comment on #2338 via post-stamped. git push not counted. Review request for huangyiirene not sent (relay pr_create carries assignees only; the order bars MCP writes) - the seat may add it.",
     "open_questions": [],
     "out_of_scope_findings": [
      "class: a · reach: public door - adding, changing or removing a line item of an accepted or expired quote answers 'Quote QTE-NNNN - NAME is accepted, so its line items can no longer be added, changed or removed.', the status API value shown to users (same family as items 12, 14, 15) · evidence: src/revenue/objects/quote.hook.ts:105; test/quote-line-freeze.test.ts:103 pins that sentence · dedupe words: quote line freeze accepted status label, quote line item refusal userMessage, settled quote lines refusal copy · carrier: #2338 (same family; seat decides item 16 or a card)",
      "carrier: none · noted, not filed: the grep the order asked for read every refuse( call under src/; no other hook refusal shows an API value (the rest name labels, record titles, counts or dates)"
     ],
     "deviations": [
      "Surface extended at the seat's word (00:17Z): test/lead-duplicate-management.test.ts and the src/sales/objects/_refusal.ts note; both declared in the PR.",
      "Commit 38a41be carries objectstack-fleet[bot] as git author through a local -c user.name/email override; later commits use the container default. No history rewrite; the squash merge decides the landed author.",
      "Worktree hotcrm-issue-2338-p14 kept until the seat's ACCEPT in case of a rework round; dev server stopped (process group killed)."
     ]
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Item added · repo:hotcrm seat 1 · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-28T04:3xZ

    PR #2354's report (6460657794) found one more refusal in item 12's family. It joins this card as item 16, p3, for a later order.

    # Reach Evidence Target
    16 Editing a line item on a settled quote returns the raw quote status, accepted or expired. src/revenue/objects/quote.hook.ts:105; test/quote-line-freeze.test.ts:103 pins the sentence. Use item 12's shape: keep the diagnostic message and add a userMessage that names the status by its label.

    Card state: items 14–15 are PR #2354, ACCEPTed with auto-merge armed. Item 16 keeps the card open.


    Generated by Claude Code

  14. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · #2338 item 16 · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-28T04:51Z

    This order takes the slot freed by #2352, which is now pm:blocked on objectstack#27086 and #27087. Lane concurrency is 2, per Irene's 「后续并发降到2」. The other slot is #2327. The order covers item 16 only, as added in comment 6460686188.

    Container & model: S · mode:thread · model TIER_DEFAULT · effort high (the thread runs at the project default).
    Role file: objectstack-ai/objectstack .claude/agents/os-dev.md. Read it in full. hotcrm AGENTS.md binds. Re-measure every file:line against origin/main (08bbdd5 or later).
    Writes:

    • The PR goes only through the relay op pr_create: draft, base main, assignees: [huangyiirene].
    • Comments go only through scripts/pm/post-stamped.mjs.
    • ⛔ No GitHub MCP write. ⛔ No issue filing: out-of-scope findings go in the report.
    • The write budget is tight, so make two writes: the PR and the report.

    Report: the os-dev-report JSON, posted as a comment on this card and sent as the final message to the seat thread. ⛔ Start editing only after the newest Claim: on this card names your branch for item 16. Push work in progress at each milestone, so a stop loses nothing.

    Ruling (not to be re-decided)

    PM mechanism assumptions

    1. The console shows userMessage in the toast for a line-item edit on an accepted quote. Measure it once in the browser. A seeded accepted quote should exist; if none does, pin the site on both test paths and say so.

    Serial constraints: none known. #2327 touches docs and the app.ts packs, not this hook.


    Generated by Claude Code

  15. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed (items 14–15) · repo:hotcrm seat 1 · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-28T04:52Z

    Reading Probe Result
    PR #2354 GET /pulls/2354 Merged 2026-10-28T04:29Z as 08bbdd5 from head 946467d1ca, the head the seat ACCEPTed (6460672060).
    main CI on 08bbdd5 GET /commits/08bbdd5/check-runs All 5 runs report success.

    State: both refusals now use labels. Editing a converted lead names the fields ("Cannot change Company …; only its Description and Notes can still be edited."). Editing a closed deal's line item names the stage ("Closed Won"). The diagnostics and the RECORD_LOCKED / 409 code are unchanged.

    Items 1–15 have landed. Item 16 (the quote line-item freeze shows a raw status) is ordered next in the same stroke, so the card stays open and keeps pm:dispatched.


    Generated by Claude Code

  16. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01J8VVAnPkgK6CWrJa6t7Fk5
    Account: huangyiirene
    Branch: claude/project-thread-crw4h8
    Worktree: hotcrm-issue-2338 (card thread; branch is platform-designated, the measured platform exception to claude/issue-N-slug; implementing session session_01J8VVAo57nVvt62WhSprraZ)
    Domain: repo:hotcrm (single-lane repo, no domain:*)
    Seat: repo:hotcrm#1
    File surface: item 16 only: src/revenue/objects/quote.hook.ts, src/_refusal.ts (site-count note only), test/quote-line-freeze.test.ts, test/refusal-labels.test.ts, test/refusal-envelope.test.ts, one changeset (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (sister repo, hand-derived), effort: the order asked high; the thread runs at the project default, which only the maintainer sets
    Clause-②: no
    Metadata-protocol: none
    Responsibility: #2338 item 16: the quote line-item freeze names the status by its label (userMessage), diagnostic and RECORD_LOCKED/409 unchanged
    Thread-read: 6460980185
    Premise: holds — re-read at claim on hotcrm origin/main 08bbdd5 (order comment 6460960959)
    Serial constraints cleared: none — #2327 touches docs and app.ts packs only

  17. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 2338,
      "status": "done",
      "branch": "claude/project-thread-crw4h8",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/2356",
      "session": "session_01J8VVAo57nVvt62WhSprraZ",
      "premise_still_valid": true,
      "summary": "Item 16 in one draft PR (Part of #2338 item 16). The settled-quote line freeze in quote_workflow keeps its diagnostic message ('is accepted') and now passes a userMessage naming the status by crm_quote.status's label ('Quote QTE-0002 - Acme Annual Renewal 2025 Quote is Accepted, so its line items can no longer be added, changed or removed.'); RECORD_LOCKED/409 unchanged. STATUS_LABELS moved to the top of the handler so the quote freeze (item 12) and the line freeze read one table; refusal-labels asserts exactly one copy and holds it to crm_quote's option labels. refusal-envelope gains this site's userWording pin on the shipped and in-process paths (site count stays 35: the site already existed); quote-line-freeze pins userMessage on every refusal; _refusal.ts note says five sites. The PM assumption held: measured in the browser on the shipped artifact, the console shows the label sentence in both the dialog banner and the toast.",
      "tests": "head 39e8270: pnpm exec vitest run (full suite) -> Test Files 237 passed (237), Tests 4571 passed | 1 skipped (4572). Touched files (quote-line-freeze, refusal-labels, refusal-envelope) -> Test Files 3 passed (3), Tests 49 passed (49). Ablation: removed the line freeze's userMessage argument (grep -c marker 1 -> 0 on disk), quote-line-freeze + refusal-envelope -> ABL_EXIT=1, 12 failed of 44 (every quote-line-freeze refusal case plus both new envelope cases); restored with git checkout HEAD --, git diff HEAD empty, marker back to 1. No dist ablation needed: hotcrm tests lower the hook from source.",
      "gates": "hotcrm has no dispatch-gates.mjs; hand-derived from package.json's verify chain, each run separately on 39e8270 with exit captured before any pipe: validate 0, typecheck 0, lint (os lint --strict) 0, hygiene 0, hygiene:tokens 0, build 0, test 0 (above). CI check-runs on PR #2356 at report time: Vercel Preview Comments success; Label Pull Request, Quality Checks, Analyze Code (javascript), Check Changeset, Build and Test (22.x), link-check, Playwright all in_progress.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "1 — pull_request_read get_check_runs (read only); no MCP write",
      "api_writes": "2 relay/REST writes besides git push — fleet-write pr_create (POST /repos/objectstack-ai/hotcrm/pulls, then POST /repos/objectstack-ai/hotcrm/issues/2356/assignees, one stroke, read back identical) and this report via post-stamped (POST /repos/objectstack-ai/hotcrm/issues/2338/comments)",
      "open_questions": [],
      "out_of_scope_findings": [
        "Post-fix grep of every refuse( call under src/: no hook refusal shows users an option API value any more; the rest name labels, record titles or counts.",
        "Observation, not a class a/b/c finding: four refusals interpolate stored date values verbatim — task_completion's reminder refusal (reminder_date is a datetime, so ISO form; read from source, not measured), contract_validation's term and End Date refusals and campaign_validation's Start/End Date refusal (YYYY-MM-DD). Machine formats, not option API values. carrier: none · noted, not filed. dedupe words: refusal date format, reminder_date ISO, userMessage date"
      ]
    }

    Generated by Claude Code

  18. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed (item 16) · closing · repo:hotcrm seat 1 · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-28T05:45Z

    Reading Probe Result
    PR #2356 GET /pulls/2356 Merged 2026-10-28T05:26Z as f660fd8 from head 39e8270fd2, the head the seat ACCEPTed (6461403109).
    main CI on f660fd8 GET /commits/f660fd8/check-runs All 5 runs success.

    State: all 16 items are landed:

    Items PR
    1–4 #2346
    5–13 #2347
    14–15 #2354
    16 #2356

    Editing a line on an accepted or expired quote now names the status by its label ("is Accepted"). The diagnostic and RECORD_LOCKED / 409 are unchanged. The post-fix grep finds no refusal that still shows users an option API value. The seat closes this card as completed and removes its lane label 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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions