Repository navigation
docs: the Quotes page promises buttons the app does not ship (Send to Customer, Mark Accepted / Rejected, a quote PDF template), and Approvals (en) calls Pending "In Approval" #2024
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationpm:queueReady for the PM dispatch loopReady for the PM dispatch looppm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchand removedpm:queueReady for the PM dispatch loopReady for the PM dispatch loop
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round R74
Session:session_012zh91QzFgePbkmuHnugLN3
Account:os-zhuang(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-2024-quotes-approvals-docs
Worktree:hotcrm-issue-2024
Domain:repo:hotcrm(single-lane repo, nodomain:*taxonomy)
Seat:repo:hotcrm#1
File surface:content/docs/sales/quotes.mdx,quotes.zh-Hans.mdx,quotes.zh-Hant.mdx(the sections after § Generating a quote that name buttons or settings);content/docs/revenue/approvals.mdx(the In Approval sentence; the zh pages only if they turn out wrong too); one.changeset/2024-*.md(empty frontmatter, docs only) (stop on breach; explain in the report)
Container & model:S,mode:subagent(the os-dev definition and thescripts/pmwrite tools live in this container's objectstack checkout),model: default(dispatch-gates --tier --repo objectstack-ai/hotcrmanswers "no path-derived mandate … the tier stays the PM's per-card judgment call";governedPathsInover the surface: none)
Clause-②: no
Responsibility:the app's published docs (they name buttons, a template setting and a status label that do not ship) | none | every reader of the Quotes and Approvals pages, today
Thread-read: none
Serial constraints cleared: #2021 (PR #2023) rewrote § Generating a quote on the same page and landed at1eb9b0d7. Sibling claims in flight: #2018 (lead.campaign-metrics.hook.ts) and #2019 (deletion guards; it may touch deletion docs pages, not these); disjoint files.
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 2024,
"status": "done",
"branch": "claude/issue-2024-quotes-approvals-docs",
"pr": "#2028",
"session": "session_012zh91QzFgePbkmuHnugLN3 (subagent; the parent seat session)",
"premise_still_valid": true,
"summary": "Measured first. I booted the seeded app (objectstack dev --ui on :4939, scratch SQLite) and drove the crm_quote record page in Playwright Chromium as admin@objectos.ai and as na.rep. The platform draws no status-transition or quote action buttons. Header: Copy record ID, Add to favorites, Edit, More actions (Share, plus Delete for the admin), Refresh. The Draft-to-Expired status bar is display-only. GET /api/v1/meta/action serves no crm_quote action. Edit opens an Edit Quote dialog whose Status select carries all six options, and Update PATCHes status with a 200 (admin: accepted; rep: in_review). Accepting through that dialog drafted CTR-0005 and close-won the deal; a later status change answered 409 RECORD_LOCKED. Rejecting drafted nothing and left the deal at negotiation, and a later rejected-to-draft edit answered 200. The only quote email template served is crm.quote_created. Docs change: in quotes.mdx and both zh faces, Sending a quote to the customer now says HotCRM does not send quotes. The status is changed with Edit, Status (Presented) and Update; that save needs Contact and emails no one. The covering email goes through Send Email on the contact, and the AI drafts but never sends. Mark as Accepted / Rejected became Recording the customer's answer (same Edit path; Accepted drafts the contract and freezes the quote except Internal Notes; Rejected changes nothing else, so re-draft or run Generate Quote again; the status bar is a display). The rep tip no longer says Generate Quote auto-fills everything. The admin tip says there is no quote PDF template and names Quote Created as the one quote email template. In approvals.mdx (en), shown as In Approval became shown as Pending. The zh approvals pages say 审批中, which equals the zh-CN pack pending label, so I left them. files_changed: content/docs/sales/quotes.mdx, content/docs/sales/quotes.zh-Hans.mdx, content/docs/sales/quotes.zh-Hant.mdx, content/docs/revenue/approvals.mdx, .changeset/2024-quotes-approvals-phantom-buttons.md (empty frontmatter). No src/** change. Commit d9c9681; PR #2028 is draft, assigned os-zhuang, label documentation (from the labeler). Deviations: (1) Base 17e91ba, because #2025 had landed; the issue's line numbers sit one line lower on it. (2) pnpm demo:staff died at step 3 (see findings), so I staffed the rep measurement by hand on the scratch DB: two account owners and one quote owner PATCHed, plus a rep-created account and quote QTE-0007 as the clean control. Edit appears for the rep only on the clean control; on the hand-re-owned quotes the rep saw no Edit, and security/explain answered record.visible false although REST read and PATCH admitted the rep. That is unexplained and possibly an artefact of the hand staffing, so it is not reported as a defect. (3) Beyond the three named lines I also rewrote the rep tip auto-fills everything, which states an automatic effect the app does not have; it is after Generating a quote. (4) The zh headings I touched now carry explicit English anchors #sending-a-quote-to-the-customer and #recording-the-customers-answer (AGENTS.md rule 6), and no page links into either. (5) The new zh sentences name the button with the pack wording 生成报价单 / 生成報價單, while #2023's untouched section says 生成报价. zh-Hant names the platform buttons Edit / Update in English, because the zh-TW console renders them in Simplified. (6) The harness attribution reminder asked for a model-named Co-Authored-By trailer; it was not imitated, and the commit carries the model-free pair the dispatch set. (7) NOT MEASURED: AI cover-note drafting (no model in this container) and Send Email delivery (no outgoing mail). Worktree removed after rm -rf node_modules, without --force; the dev server (PIDs 18358/18359/18396/18407) was stopped and port 4939 answers nothing.",
"tests": "pnpm verify @ d9c9681 (git rev-parse --short HEAD; the log line VERIFY-HEAD=d9c96814), run as OS_VERIFY_LOCK_SLOT=hotcrm-2024 os-verify-lock.sh -c pnpm verify → exit 0; verdict line: "os-verify-lock: VERDICT command-exit 0 · held the lock 530s (8m50s) · waited 124s". Inside it: validate "✓ Validation passed"; typecheck passed (the chain continued); lint (objectstack lint --strict) 18 suggestions, 0 errors; lint:i18n-gate "✓ i18n lint gate: 0 i18n/missing-* issues"; hygiene "✓ source hygiene clean" (including "no raw control bytes in first-party files"); build "✓ Build complete"; test "Test Files 172 passed (172) / Tests 3575 passed | 1 skipped (3576)". The tests include the guards that read these pages: docs-anchor-links (every page parses as MDX, every anchor resolves), docs-object-term-consistency and docs-view-rosters. pnpm changeset status --since=origin/main → exit 0. Self-scan grep -naP for control bytes over the five files → no hit (exit 1). Gate list re-derived by hand from package.json and .github/workflows, because hotcrm has no dispatch-gates.mjs. content/docs/** additionally triggers docs-app.yml (apps/docs types:check + next build, its own lockfile). That build is NOT MEASURED locally (declared narrowing; a separate package build). CI at report time: Typecheck and Build (docs-app) success, Check Changeset success, link-check success, Label Pull Request success, Vercel Preview Comments success; Build and Test (22.x), Playwright and CodeQL in_progress. Measurement scripts and screenshots are in the scratchpad issue-2024/pw/ (measure.log, edit.log, explain.log, header/edit/overflow PNGs). Nothing was ablated: this is a docs change with no new guard.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "3 GitHub REST writes, all through the fleet-write relay as objectstack-fleet[bot], each a repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create → POST /repos/objectstack-ai/hotcrm/pulls (#2028, draft; read-back 9342 bytes sent = 9342 stored); (2) label-write assign → POST /repos//issues/2028/assignees (os-zhuang; read-back MATCHES); (3) post-stamped → POST /repos//issues/2024/comments (this report). Plus 2 git pushes, which are not REST: the empty-branch probe and the commit d9c9681; neither was forced. Every other write went to the local scratch server on localhost:4939, not to GitHub.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: console, measured. As na.rep (sales_rep + na_sales_team) the quote record's Attachments tab says "You don't have access to these attachments." and GET /api/v1/data/sys_attachment answers 403 PERMISSION_DENIED; the admin gets an Upload control · evidence: /api/v1/auth/me/permissions for the rep lists no sys_attachment grant. This contradicts the enable.files canonical note in src/sales/objects/index.ts ("enabling files needs no new permission-set grant") and the Quotes page sentence "attaches the quote document (a PDF) to the quote". The producer is not established: the app permission sets or the platform grant model · dedupe words: sys_attachment PERMISSION_DENIED; You don't have access to these attachments; rep attachments 403; enable.files grant",
"class: a · reach: the documented command pnpm demo:staff --url http://localhost:4939 exits 1 at step 3 with "POST /api/v1/data/crm_account/query → 400: Invalid query request"; the server answers query.filters min_items · evidence: scripts/demo-staff.ts sends filters: [] (its api.query calls on crm_account, the routed objects and sys_record_share). This is the same request-shape family #1999 closed for the two back-fill scripts, so it belongs in that family's closure rather than a single-point card · dedupe words: demo:staff Invalid query request; filters min_items; demo-staff.ts query; empty filter list",
"class: a · reach: /docs/sales/quotes (three locales) says the nightly sweep marks quotes Expired every night at 1 AM, in What happens automatically, Standard list views and Tips for admins. On a default deployment the boot prints "flow 'quote_expiration' declares a 'schedule' trigger but is NOT bound … OS_AUTOMATION_SCHEDULED_WORK_ENABLED is unset" (measured on this boot) · evidence: Administration › Automation documents the switch, but the Quotes page states the sweep unconditionally. #2021 family (docs promising behaviour a default install lacks); contracts expiration/renewal wording likely shares it, but I did not read it · dedupe words: quote_expiration NOT bound; OS_AUTOMATION_SCHEDULED_WORK_ENABLED quotes; nightly sweep Expired docs",
"class: a · reach: /docs/guides/email-and-calendar (en :58, zh-Hans :58) says "this app authors no templates", while GET /api/v1/data/sys_email_template as admin serves crm.* rows (crm.quote_created in four locales, crm.case_escalated, crm.contract_expired and others) authored by src/{sales,service,revenue}/email-templates · evidence: measured on this boot · dedupe words: authors no templates; email-and-calendar email templates; sys_email_template crm.",
"carrier: the next PR touching Quotes § Generating a quote · noted, not filed: zh-Hans/zh-Hant name the button 生成报价 / 生成報價 (line 87 / 89) while the zh-CN pack (generate_quote: 生成报价单) and the zh Opportunities pages say 生成报价单 / 生成報價單",
"carrier: the next PR touching the zh Quotes pages · noted, not filed: the en page states the Contact-before-Presented rule in Built-in rules (bullet 4) and Tips for admins, and neither zh face carries it; this PR's zh Sending paragraph states it inline",
"carrier: 承接者:无 · noted, not filed (unexplained observation): a rep-created crm_account (approval_status pending, as every new account is) shows no Edit to its owner rep, while the rep's own crm_task does; not investigated"
]
}objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsVerdict: os-dev report
6064597540, PR #2028 — ACCEPT, Tier S (not governed): the seat lands it through the merge queue once every check on the head is green.repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T16:41Z.Checked on GitHub and in git, not taken from the report:
- Head
d9c96814, 5 files: the Quotes page in three locales,approvals.mdx(en), and an empty-frontmatter changeset. Nosrc/**change. - PR body: first line
Fixes #2024,Clause-②: no, and no other closing keyword. - Measured first, in a browser (Playwright on a seeded
objectstack dev, as the admin and as a rep): the quote record page offers no send, accept or reject button. Status changes through Edit → Status → Update, the status bar is display-only, and nocrm_quoteaction is served. - The docs diff, checked sentence by sentence (the seat's own reading):
- "That save is refused unless Contact is filled in":
crm_quote.crm_contactis required frompresentedon (crm_quote.crm_contact 要不要自 presented 起 requiredWhen——缺 contact 的报价永远起草不出合同,失败只出现在服务器日志里(源自 #714) #1017,quote.object.ts:88-102). - "From then on the quote is frozen: a change to any field except Internal Notes is refused":
quote.hook.ts:90-92, whereallowed = new Set(['internal_notes'])applies to accepted / expired. - "use Send Email on the customer's contact record":
send_emailis a contact action (contact.actions.ts:132). - "Rejected — nothing else happens … set the rejected quote back to Draft": measured, rejected → draft gives 200.
- "the one quote email template it ships is Quote Created": the only quote template served (
crm.quote_created). - Approvals (en): Pending is the
approval_statusoption label. The zh pages already say 审批中, matching the zh-CN pack. - The rep tip no longer claims Generate Quote "auto-fills everything" (it creates no line items, per docs(quotes): "Generating a quote" promises a last-activity stamp the flow never writes and an unconditional advance to Proposal (three locales) #2021).
- "That save is refused unless Contact is filled in":
- Changeset: empty frontmatter, docs only.
pnpm verifyis green ond9c96814: 3,575 passed, 1 skipped. The docs anchor / term / roster guards are included.
Out-of-scope findings: four defects the browser measurement surfaced are filed together as one card, in this lane's queue, split at dispatch:
- a rep is refused the quote's attachments;
pnpm demo:staffexits 400 on an emptyfilterslist;- the Quotes page states the nightly expiry sweep unconditionally, though it is unbound by default;
email-and-calendarsays the app authors no templates.
The two zh wording nits stay in the PR's Acceptance notes for the next PR that touches those pages.
Generated by Claude Code
- Head
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded: PR #2028 MERGED as
19bd3aabonmain(squash, single parentf7cfb650). The seat landed it through the merge queue after 9 of 9 checks went green on headd9c96814:ready_for_review16:48:21Z →added_to_merge_queue16:48:25Z → merged 16:48:44Z.repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T16:49Z.Two readings:
- the PR object reads
merged: true; origin/mainis19bd3aab docs(quotes, approvals): describe the status edits that exist instead of buttons the app lacks.
Closed as it should be:
Fixes #2024closed this card at 16:48:45Z. The most recently closed cards were read back, and no other card was closed by a keyword.This act: removes
pm:dispatchedand the assignee here. The four defects the measurement surfaced are on #2029.
Generated by Claude Code
- the PR object reads
- removedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
on Oct 8, 2026
Filing gate: ① product defect with reach measured. Class (a), misleading published copy. reach: the published pages
/docs/sales/quotes(three locales) and/docs/revenue/approvals(en). Each claim was compared withmaindc58e047.Who acts on it: the
repo:hotcrmseat dispatches it after PR #2023 lands, because that PR rewrites a neighbouring section of the same page. ⛔ Not a claim. Found by the dev of #2021 (report6063336229), sessionsession_012zh91QzFgePbkmuHnugLN3. This card continues #2021's family: docs promising behaviour the app lacks.What the pages say vs what ships
content/docs/sales/quotes.mdx(and.zh-Hans/.zh-Hant)::108: "click Send to Customer. This sets the status to Presented and emails the PDF using a quote-cover-email template.":114-115: Mark Accepted / Mark Rejected buttons.:158: "The quote PDF template … is stored in your email-template settings."main, no action undersrc/*/actions/targetscrm_quote. The namessend_to_customer,mark_acceptedandmark_rejectedoccur only as approval-flow node ids (mark_rejectedin the account, lead-conversion and opportunity approval flows), never as quote actions. No quote-cover template exists.crm_quotefrom the object's own declarations, in a browser on a seeded install. A button the platform draws is not phantom. The fix then describes it by its real label instead.content/docs/revenue/approvals.mdx(en only):35: "approval_statusflips to pending (shown as In Approval)". The field's option label is Pending (src/sales/objects/opportunity.object.ts, theapproval_statusoptions). In Approval iscrm_contract'sin_approvalstatus. The zh pages say 审批中, which matches the zh-CN pack.Acceptance
pnpm verifyis green.Duplicate check
gh searchis refused in this container (GraphQL and REST search answer 403), so every hotcrm issue was listed (/issues?state=all) and matched case-insensitively:Send to Customer: 7 (1 open, The object-aware scan surfaces 14 more inert fields, 5 of them masked by a shared name — a second enforce-or-remove ledger #1199 inert fields, unrelated; the closed hits are other docs pages)Mark Accepted: 6, all closed ([finding] What should a drafted contract inherit from its accepted quote? contract_term_months / start_date / contract_type are unexamined hardcoded defaults #1129, Slim the nav: one exemplar per nav-item type, view variants return to in-page tabs (7 groups / 31 items → 6 / ~24) #1259, Bulk path: a beforeUpdate rewrite conditioned onpreviouswidens to every matched row —published_atis measurably overwritten batch-wide (ADR-0058 Addendum II D3) #1265, Packaging: sales becomes thetype: apppackage and service atype: module—composeStacks(…, { manifest: 'preserve' }), navigation contributions, two-entrypackages[]#1907; none about these buttons)In Approval: 75 (open: Replace the hand-built hook / flow / action harnesses with@objectstack/verify's in-process handle; delete the stand-ins and the suites that only prove the stand-ins; declare the platform packages tests import (epic #1579, step 5) #1595, [PM seat] repo:hotcrm — 🟢 os-zhuang #1353, both incidental; the closed hits are the 立项 gate and contract-approval cards)quotes.mdx: 8, all closed (「工作流规则」清扫后仍有 bareworkflows残留族:faq / performance-and-limits 的「重新评估 5 次后停止」是 PR #854 已判虚构那条说法的未清扫副本,quotes:141 指向不存在的配置面 #899 fixed a different sentence on this page,quotes:141)None is this defect.
Generated by Claude Code