Repository navigation
docs(flows): name what the flow nodes do, not what they were once meant to do - #877
Merged
Merged
Conversation
…nt to do (#869) #851 corrected the Automation page's flow table and the `opportunity_won_alert` `description`. The same three claims survived one layer further in — on the nodes themselves, which ship as authored metadata inside `dist/objectstack.json`, and in a file-header comment that had begun contradicting the description eleven lines below it. - `Notify Management` -> `Notify Owner` in `opportunity-won-alert.flow.ts`. The node addresses `{record.owner_id}` and nothing else. The comment directly above it already explained why there is no manager recipient: `{record.owner_id.manager}` cannot traverse a lookup on the raw trigger snapshot, so it interpolates to the literal `undefined` and the message is delivered to a phantom user. The label was contradicted by its own header. `Notify Owner` is also the label four other flows already use for this node. - `Assign to Senior Agent` -> `Flag as Escalated` in `case-escalation.flow.ts`. The `update_record` node writes `is_escalated`, `escalation_reason`, `escalated_date` and `status`, and never touches `owner_id`; the comment inside it opens with "No owner reassignment". The case stays with the agent who had it, exactly as the escalation notice tells its recipient: "It remains assigned to you." - `Notify Support Team` -> `Notify Case Owner` in `case-escalation.flow.ts`. Same file, same class of claim: `recipients` is the single entry `{caseRecord.owner_id}`, not a team. The identical node in `case-sla-monitor.flow.ts` — same `notify_team` id, same owner-only recipient — already carried the honest label `Alert Owner`; this one had been left behind. The file-header comment on `opportunity-won-alert.flow.ts` still described the flow as "notify the owner and their manager", the sentence #851 removed from the `description` below it. It now carries that same correction. Node ids are deliberately untouched. `notify_management`, `assign_senior_agent` and `notify_team` keep their original spellings because `edges[]` reference nodes by id and `CaseEscalationOnCreateFlow` rewrites the node list by id. Renaming one would be a behaviour change wearing a wording fix's clothes, so each node now carries a short note saying so, to keep the next reader from tidying an id that looks stale beside its corrected label. Labels and comments only. No recipient, condition, field set, edge or id differs, and this takes no position on whether escalation should reassign or should copy a manager — both remain open product questions under #595. Co-authored-by: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
yinlianghui
marked this pull request as ready for review
August 6, 2026 03:04
This was referenced Aug 6, 2026
src/docs/crm_sales.md 两处仍称「通知负责人及其经理」,两条 flow 都只发负责人 —— 且该文件随 ADR-0046 打进 dist/objectstack.json
#875
Closed
Closed
Closed
docs(service): write the index page's 《Where to find things》 list to the real navigation (#927)
#932
Merged
Closed
yinlianghui
added a commit
to yinlianghui/hotcrm
that referenced
this pull request
Aug 10, 2026
…the real navigation (objectstack-ai#927) (objectstack-ai#932) The section that tells a new user where to click named four items; three of them did not survive a read of `src/apps/crm.app.ts`, and the one real item the group does carry was missing. Measured against `src/apps/crm.app.ts:131-140`, `group_service` has exactly three children: `nav_case` (Cases), `nav_knowledge` (Knowledge) and `nav_service_dashboard`, whose label is **Service Overview** — not "Service Dashboard". `crm_task` has no entry in this group at all: its two nav items are `nav_my_tasks` (My Tasks) and `nav_all_tasks` (All Tasks), both under `group_work` / **My Work** (`:84` / `:93`). And no metadata anywhere carries the name *Service Board*: the kanban is the view `case_workflow` with `label: 'Service Workflow'` (`src/views/case.view.ts:72-75`), reached from the **Workflow** tab in the case list's view switcher (`:59`) and never from the sidebar. So the list now names the three real items with their real labels, adds the Knowledge entry it had been dropping, and keeps the two names readers will arrive with — Tasks and Service Board — pointing at where those things actually live, per the objectstack-ai#870 / objectstack-ai#877 / objectstack-ai#885 / objectstack-ai#894 / objectstack-ai#913 / objectstack-ai#924 convention of naming what does not exist rather than deleting it silently. Product questions are left open on purpose (objectstack-ai#595 / objectstack-ai#596): whether the kanban should become its own nav item, and whether Tasks belongs under Service, are not decided here — only the current shape is recorded. All three locales, same lines. `src/` untouched. Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui
added a commit
to yinlianghui/hotcrm
that referenced
this pull request
Aug 10, 2026
…to the real navigation (objectstack-ai#943) (objectstack-ai#953) The section that tells a reader where to click named four things; three of them do not survive a read of `src/apps/crm.app.ts`, and the one that does was labelled with a name the app never shows. Measured against `src/apps/crm.app.ts` on `origin/main` (b7791ca, platform 17.0.0-rc.3), the app has seven groups — Sales, My Work, Activity, Marketing, Service, Insights, Approvals — and none of them is **Products**. The catalog's only sidebar entry anywhere is `nav_product`, labelled **Products**, under `group_marketing` (`:126`); `grep -rn "crm_product" src/apps/` returns that one line. So the page now sends a reader arriving with the name "Products group" to Marketing instead of to a place that does not exist, and links the Marketing overview page that objectstack-ai#938 / PR objectstack-ai#942 just brought in line. `group_approvals` (`:159-171`) has exactly one child: `nav_approval_requests`, whose label is **Inbox** (`:165`) — not *Approval Requests*, which no metadata in this repo carries. The other two listed items are re-judged individually rather than deleted silently, per the objectstack-ai#870 / objectstack-ai#877 / objectstack-ai#885 / objectstack-ai#894 / objectstack-ai#913 / objectstack-ai#924 / objectstack-ai#932 / objectstack-ai#942 convention: - **Action History** — no nav item of that name exists. The data behind it does: @objectstack/plugin-approvals registers `sys_approval_action` (enumerated from the installed package, alongside `sys_approval`, `sys_approval_approver`, `sys_approval_delegation`, `sys_approval_request`, `sys_approval_token`), and nothing in this app's navigation opens it. The page says exactly that. - **Processes** — deleted on purpose, and the source records why at `:166-169`: no `sys_approval_process` object exists in any installed plugin, so the old item's `requiresObject` guard hid it on every install, forever. The enumeration above confirms the absence, so the reason is written into the page. The one true claim survives unchanged: **Contracts** is under **Sales** (`nav_contract`, `:60`), and it is the app's only sidebar route to a contract. Two further facts from source are recorded with it: `group_marketing` and `group_approvals` declare no `expanded` key while Sales, My Work, Activity and Service set `expanded: true`, and `GroupNavItemSchema.expanded` defaults to `false` — so both groups are collapsed on load, which is the failure mode that sends a reader looking for the catalog away empty-handed. And the zh-Hans page names the label a simplified-Chinese user actually sees, 待我审批 (`src/translations/zh-CN.ts:1219`), next to the source label. Product questions stay open on purpose (objectstack-ai#595 / objectstack-ai#596): whether Products deserves its own group and whether Approvals should gain an audit-trail item are product decisions, not documentation ones. Only the current shape is recorded. All three locales, same lines; zh internal links carry no anchor. `src/` untouched. Fixes objectstack-ai#943 Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui
added a commit
to yinlianghui/hotcrm
that referenced
this pull request
Aug 10, 2026
… real app (objectstack-ai#960) (objectstack-ai#968) The first table a new user reads, whose entire job is "here is what the sidebar holds", had drifted in every one of its eight rows. Measured against `src/apps/crm.app.ts` on `origin/main` (4705aed, platform 17.0.0-rc.3), the app's `navigation` is one pinned top-level entry plus seven groups: `nav_home` (Home, :34), `group_sales` (:42), `group_work` (My Work, :69), `group_activity` (Activity, :103), `group_marketing` (:120), `group_service` (:131), `group_insights` (Insights, :144) and `group_approvals` (:160). Row by row, the old table said: - **Sales** — the group is real, but it carries nine children, not six. The table dropped Account Workbench (:52), Pipeline (:55) and Sales Performance (:61). - **Service** — real; the entry the table called *Knowledge Base* is labelled **Knowledge** (:138), and **Service Overview** (:139) was missing. Both names were already written to source on `service/index` by objectstack-ai#927 / PR objectstack-ai#932 and objectstack-ai#937 / PR objectstack-ai#947, so the two pages contradicted each other. - **Marketing** — real, but its children are Campaigns (:125) and Products (:126). *Campaign Members* is not a navigation entry at all: `grep -rn "crm_campaign_member" src/apps/` returns nothing. - **Products** — no such group. The catalog's only sidebar entry is `nav_product` under `group_marketing`, the same finding objectstack-ai#938 / PR objectstack-ai#942 and objectstack-ai#943 / PR objectstack-ai#953 already wrote to two other pages. - **Activities** — no such group; the real one is **Activity**, and `crm_task` is not in it. Its two entries are My Tasks (:84) and All Tasks (:93), both under **My Work** — which the table never mentioned at all, though it is the group a rep uses every day. - **Analytics** — no such group; the real one is **Insights**, and no entry anywhere is labelled *Dashboards* or *Reports*. - **AI** — no such group anywhere in the repo, and no entry labelled *Copilot* or *Knowledge Bases*. `src/apps/` contains one file; neither name appears in it. - **Approvals** — real, with exactly one child, labelled **Inbox** (:165). *Approval Requests* and *Action History* carry no metadata in this repo; objectstack-ai#943 / PR objectstack-ai#953 recorded the same two names on the revenue page. So the table now lists the pinned Home entry and all seven groups with their real children in source order, and every retired name is re-pointed rather than deleted silently, per the objectstack-ai#870 / objectstack-ai#877 / objectstack-ai#885 / objectstack-ai#894 / objectstack-ai#913 / objectstack-ai#924 / objectstack-ai#932 / objectstack-ai#942 / objectstack-ai#953 convention. Two further facts from source ride along: `group_marketing`, `group_insights` and `group_approvals` declare no `expanded` key while Sales, My Work, Activity and Service set `expanded: true`, and `GroupNavItemSchema.expanded` defaults to `false`, so those three are collapsed on load — the failure mode that makes a reader conclude something is absent. And the zh pages name the labels a simplified-Chinese user actually sees (待我审批, 知识库, 我的工作 — `src/translations/zh-CN.ts:1195-1219`), which is also why the old English *Knowledge Base* read plausibly for so long. Nothing checked any of it: `os validate` and `pnpm lint` walk authored metadata and never open `content/docs`. `test/docs-quick-tour-navigation.test.ts` now compares the table to `CrmApp.navigation` group-for-group and child-for-child in all three locales, pins the bold-is-real / italic-is-phantom typography the sibling pages already use, and pins the source facts the prose rests on. Restoring the old table turns 15 of its 21 assertions red in the predicted direction. Product questions stay open on purpose (objectstack-ai#595 / objectstack-ai#596): whether Products or AI deserve their own groups is a product decision, not a documentation one. Only the current shape is recorded. All three locales, same section; zh internal links carry no anchor. `src/` untouched. Fixes objectstack-ai#960 Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #869
#851 / PR #870 corrected the Automation page's flow table and the
opportunity_won_alertdescription. The same claims survived one layer further in — on the nodes themselves, which ship as authored metadata insidedist/objectstack.json, and in a file-header comment that had begun contradicting the description eleven lines below it. Same "write the current behaviour" standard, same wording, no third phrasing.Premise re-check against the current baseline
Baseline
origin/main= 6cd53d2, which contains PR #870 (5868728) and #874. All three sites re-located after #870's edits and all three are still present:opportunity-won-alert.flow.ts:71labelNotify Managementcase-escalation.flow.ts:87labelAssign to Senior Agentopportunity-won-alert.flow.ts:10-12JSDoc "notify the owner and their manager"Node id reference chains, checked before editing (the issue flagged these as load-bearing):
No id is touched. Diffing the
id:/source:/target:lines againstorigin/mainshows every id string byte-identical; onlylabel:values and comments differ.The three sites
src/flows/opportunity-won-alert.flow.tsNotify ManagementNotify Ownerrecipients: ['{record.owner_id}']— the owner and nobody else. The comment directly above the node already explained why there is no manager recipient:{record.owner_id.manager}cannot traverse a lookup on the raw trigger snapshot, so it interpolates to the literalundefinedand the message goes to a phantom user. The label was contradicted by its own header.src/flows/case-escalation.flow.tsAssign to Senior AgentFlag as Escalatedupdate_recordnode writesis_escalated,escalation_reason,escalated_date,status. It never touchesowner_id; the comment inside it opens withNo owner reassignment:.src/flows/opportunity-won-alert.flow.ts:10-12descriptioneleven lines below, so the two no longer disagree.Notify Owneris not a new coinage:task-urgent-alert,task-due-reminder,contract-renewalandcontract-expirationalready label this node exactly that.One further label in the same file
The dispatch allows same-file labels that are equally untrue to be corrected in the same pass, listed here:
src/flows/case-escalation.flow.ts— labelNotify Support Team->Notify Case Owner.recipientsis the single entry{caseRecord.owner_id}, not a team. The decisive evidence: the identical node insrc/flows/case-sla-monitor.flow.tscarries the samenotify_teamid and the same owner-only recipient, and its label already readsAlert Owner— this one had simply been left behind. A prior pass had already corrected this node's message body ("reassigned" was also false: this flow never changes the owner) without correcting its label.Why the ids keep their old spellings
notify_management,assign_senior_agentandnotify_teamnow each carry a short comment recording that the id is deliberately stale-looking. After this PR every one of them reads like a drift a future agent might "tidy" — and renaming any would be a behaviour change wearing a wording fix's clothes.Artifact landing
pnpm buildthen scanningdist/objectstack.json:The 2-hit counts are correct rather than duplicated:
CaseEscalationOnCreateFlowbuilds its node list fromCaseEscalationFlow.nodes.map(...), so both flows carry the corrected nodes.Do any tests pin these labels?
No — confirmed, as the dispatch predicted.
test/automation-docs-coverage.test.tsderives its expectations from each flow's own top-levellabel(labelFor = (flow) => flow.label), plus row sets, trigger cells and counts. Node labels never enter it.test/docs-drift.test.tsextracts only numeric thresholds and cron schedules. A repo-wide grep for the three literals returns the three source lines and nothing else — no test, fixture or doc quotes them.Re-ran both guards on their own to be sure:
2 passed (2) / 53 passed (53).A "revert it and watch the tests go red" check is therefore not available for this change, and reporting one would be fabrication: nothing pins node labels, which is precisely why these three drifted unnoticed. The load-bearing evidence here is the artifact scan above (old strings provably gone, ids provably intact) plus the guards staying green in the direction predicted before running them.
Not verified (carried over from the issue, unchanged)
The issue states it did not verify whether the process monitor / flow run detail actually renders node labels, and this PR does not close that gap either. The fix stands on the labels being authored metadata published in the artifact — a false label is worth correcting whether or not that particular surface paints it.
Verification
All six gates run under the shared lock with
NODE_OPTIONS=--max-old-space-size=4096:pnpm validatepnpm typecheckpnpm buildBuild complete (2162ms),Artifact: dist/objectstack.json (1921.4 KB)pnpm test --maxWorkers=2Test Files 66 passed (66)/ `Tests 1587 passedpnpm lintpnpm hygieneno raw control bytes in first-party filesControl-byte self-scan over the three changed files, beyond the gate's scan surface: 0 hits.
Scope
Labels and comments only. No recipient, condition, field set, edge or id differs, and this takes no position on whether escalation should reassign or should copy a manager — both remain open product questions under #595.
Filed while verifying, not fixed here: #875 (
src/docs/crm_sales.mdmakes the same manager claim twice, and that file ships in the artifact via ADR-0046) and #876 (content/docs/service/sla-and-escalationdescribes escalation as a reassignment plus a three-way mailout, in three languages).Generated by Claude Code