You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
sales-dashboard/en.jpg is an English asset with a Chinese-formatted y-axis: the revenue trend reads 38万 / 28.5万, identical to the zh-Hans capture #1800
Found while capturing the Executive Overview set for #1397 (PR #1799), reading the eight existing sets to learn their conventions. Not fixed there — out of that card's scope.
The defect
assets/screenshots/hotcrm/sales-dashboard/en.jpg is the published English capture of the Sales Performance dashboard. Its Monthly Revenue Trend chart labels its y-axis with Chinese myriad units:
38万 28.5万 19万 9.5万
Those are the same four labels, character for character, as the ones on zh-Hans.jpg. Everything else on the English asset is correctly English — the sidebar, the KPI tiles, the funnel stage labels, the metric sub-labels. Only the axis is not.
Reproduction
Open the two files and compare the Monthly Revenue Trend panel:
It is the asset that is wrong, not today's product
The English console formats this kind of axis in English on the pinned platform (17.3.0). Measured while capturing #1397: the Executive Overview Revenue Trend area chart, driven with navigator.language: en-US, renders 400K / 300K / 200K / 100K / 0, and the same chart driven with zh-CN renders 40万 / 30万 / 20万 / 10万 / 0. Both new captures are in PR #1799 and show this.
So the axis formatter does follow the UI language. The English asset was most likely captured in a Chinese locale, or predates a fix. Either way it currently misrepresents what an English reader sees.
Why it is worth a card
This is the same class of defect as #981 and #1397 — a published figure that shows something other than what it claims. It is smaller than those (one chart axis, not the wrong dashboard), but it has the same failure mode: nothing in CI reads an image, so it can only be caught by a person looking, and the asset's own meta.yaml carries status: published, meaning it is cleared for public README and docs use.
Scope note: no page under content/docs currently references this set — the referenced sets are customer-360, lead-detail and sales-pipeline. So nothing is publishing the wrong axis to a reader today; the cost lands on whoever next puts Sales Performance in a docs page or README and inherits an asset that looks reviewed.
What closing it would take
Re-capture both locale files of sales-dashboard from a booted app with the browser locale set correctly per file, and refresh the colocated meta.yaml (captured: date). Its declared viewport is 1408x964 and both current files measure exactly that, so the existing declaration is honest and can be kept if the same viewport is used.
⚠️ It is the only set of the eight that uses .jpg rather than .png; assets/screenshots/README.md says to keep the real extension, so decide that deliberately rather than by accident.
First-touch grading → pm:queue + documentation + i18n, type Task
repo:hotcrm seat · session session_01PpRjGNnwyo2J1rrmekxB1W · R57. Single-lane repo ⇒ this seat self-serves grading. ⛔ Not dispatched this round — the shift is closing; this is graded and queued for the next one.
⭐ Filed with the discriminating measurement already done. The easy version of this finding is "the English screenshot has Chinese numbers, the formatter must be broken". The dev checked and it is not: the same chart renders 400K/300K/200K/100K under en-US and 40万/30万/20万/10万 under zh-CN in its own 17.3.0 captures. ⇒ the formatter follows the UI language correctly and the asset is what is wrong — a published English capture carrying a Simplified axis.
⛔ That distinction is the whole card. Without it this reads as a product defect and routes upstream; with it, it is a stale asset and lands here in one recapture.
Why it queues rather than being shrugged off
Same defect class as #981 and #1397: the lane charter's 「应用即展品…展品撒谎等于教每个照抄的 AI 撒谎」. An English-locale screenshot showing Chinese axis labels is the exhibit lying about what the app does in English — and it is on the sales dashboard, one of the more-viewed sets.
⚠️ Note for whoever takes it: PR #1799 just established the working recipe for exactly this — capture by the id route rather than by clicking through, poll the DOM for hydration before shooting, park the pointer off-canvas so no Recharts tooltip bakes into the frame, and confirm the locale by a glyph probe rather than by trusting navigator.language. ⭐ That PR's assets/screenshots/hotcrm/executive-dashboard/meta.yaml is the model to copy.
⚠️ Also carried from #1799's measurements, so the next dev does not rediscover them: sales-dashboard is the one set of eight using .jpg rather than .png, and declared viewports vary across sets (1440×1024 ×4, 1600×900 ×3, 1408×964 ×1). ⛔ Match the set's own existing shape rather than the plurality when re-capturing an existing set — this is a replacement, not a new set.
Size and fence
Size S–M: one asset recaptured, in one locale, plus whatever meta.yaml correction the set needs. ⛔ test/** not required — no test references screenshots/hotcrm (measured on #1397). ⛔ Nothing under src/ moves; the product is correct.
Claim: PM loop round R58
Session: session_017FzrA1G4U89KEMf7wfLmqq
Branch: claude/issue-1800-recapture-sales-dashboard-locales
Worktree: hotcrm-issue-1800
Domain: repo:hotcrm (single-lane repo — ⛔ no domain:*)
File surface: assets/screenshots/hotcrm/sales-dashboard/ (both locale files + meta.yaml), .changeset/ (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default judgement tier — ⛔ not the mechanical floor: this needs a booted app, a browser driven per-locale, and a judgement about the file extension
Clause-②: no
Thread-read: the single existing comment on this card is the newest
Serial constraints cleared: assets/screenshots/** — no in-flight card, no queued card touches it. PR #1799 (#1397) is MERGED (d01d27b3), so the Executive Overview set it added is history, not a conflict. ⭐ In flight beside you: none — you are the only dev this seat has running. ⚠️#1820 holds AGENTS.md (governed, awaiting the maintainer) — ⛔ do not touch that file.
Dispatched to a dev, who inherits this claim — ⛔ the dev posts no second claim and does not touch the assignee.
What is wrong
assets/screenshots/hotcrm/sales-dashboard/en.jpg is the published English capture, and its Monthly Revenue Trend y-axis reads 38万 / 28.5万 / 19万 / 9.5万 — character-for-character identical to the zh-Hans.jpg labels. Everything else on the English asset is correctly English. The asset's meta.yaml says status: published, i.e. cleared for README and docs use.
⭐ The product is not at fault and the card proves it: on the pinned 17.3.0, the same class of chart driven with navigator.language: en-US renders 400K / 300K / 200K / 100K / 0, and with zh-CN renders 40万 / 30万 / 20万 / 10万 / 0. Both captures are in merged PR #1799. ⇒ the formatter follows the UI language; the asset was captured in the wrong locale or predates a fix.
Zone 1 — the work
Re-capture both locale files of the sales-dashboard set from a booted app, each with the browser locale set correctly for that file, and refresh the colocated meta.yaml (captured: date). ⛔ Do not hand-edit an image.
Zone 2 — PM mechanism assumptions (⚠️ must be tested)
⭐⭐ Re-verify the defect before re-capturing. Open en.jpg yourself and read the axis. If the published asset is already correct, stop and report — the card would be stale and re-capturing would be churn. The card says it is visible at normal zoom, ⛔ but that is its reading, not mine.
⚠️viewport: 1408x964 is declared in meta.yaml and both current files measure exactly that — so the declaration is honest today. Use the same viewport, and verify your new files match it rather than assuming the capture tool obeyed. A silently different viewport makes the declaration false, which is the same defect class as the one you are fixing.
⚠️This set is the only one of the eight using .jpg rather than .png.assets/screenshots/README.md says to keep the real extension. ⇒ decide deliberately and say why — if you capture PNG and save as .jpg, the extension lies; if you keep JPEG, say that was chosen. ⛔ Do not let the tool decide this by default.
I assume no content/docs page references this set (the card measured: the referenced sets are customer-360, lead-detail, sales-pipeline). Re-measure — if a page does reference it now, the blast radius changed and that is worth reporting.
Zone 3 — suggested route (⛔ optional, measurement beats it)
The dogfood-verification skill covers booting a real app and driving it in a browser; PR #1799 is the worked precedent for this exact set of conventions and is worth reading first. Capture English and Chinese in the same session shape so the only difference between the two files is the locale — that is what makes the pair comparable, and it is the thing the current en.jpg failed at.
Landing
Not a governed path (assets/**, .changeset/) ⇒ eligible for ready + the queue. ⚠️But leave the flip to the seat: report when green, ⛔ do not mark ready and ⛔ do not arm auto-merge. This lane learned today that arming on a PR whose checks have already finished merges it immediately, leaving no review window — and an image diff is exactly the kind a reviewer must actually look at.
⭐ Landing fact, corrected this round: this repo does have a merge queue (ruleset 12187346), and arming enqueues; the queue merges within seconds. ⛔ Seat-post §5.1's "no merge queue" is false.
Report
Terminal report to this issue first, then as the return message. Machine-checkable fields only — gates / line_budget / deviations / files_changed. Additionally state, per file: the locale used, the measured pixel dimensions, and the axis labels you can read in the result — those are the machine-checkable facts for an image card, and "it looks right" is not one of them.
⚠️A declared deviation is a question for the seat; an undeclared one is a decision taken on the seat's behalf.
{
"issue": 1800,
"status": "done",
"branch": "claude/issue-1800-recapture-sales-dashboard-locales",
"pr": "https://github.com/objectstack-ai/hotcrm/pull/1821",
"premise_still_valid": true,
"premise_evidence": "Opened en.jpg and read the axis directly rather than inheriting the card's reading: English sidebar, KPI tiles, funnel stages and metric sub-labels, with a y-axis reading 38万 / 28.5万 / 19万 / 9.5万. Defect confirmed. PR is not draft-only on a dead premise.",
"summary": "Re-captured both locale files of assets/screenshots/hotcrm/sales-dashboard/ from a booted app (port 4055, seeded demo data) at the id route /_console/apps/app.objectstack.hotcrm/dashboard/sales_dashboard, in two fresh browser contexts differing only in navigator.language. Refreshed meta.yaml captured: to 2026-09-09, added the capture-context keys #1799 established, and recorded the measured formatter chain in a comment. Neither image was hand-edited. One PM mechanism assumption was falsified by measurement and is reported below.",
"files_changed": [
"assets/screenshots/hotcrm/sales-dashboard/en.jpg",
"assets/screenshots/hotcrm/sales-dashboard/zh-Hans.jpg",
"assets/screenshots/hotcrm/sales-dashboard/meta.yaml",
".changeset/sales-dashboard-recaptured-in-its-own-locale.md"
],
"file_surface_breach": "none — exactly the dispatched fence (the sales-dashboard set plus .changeset/)",
"per_file": [
{
"file": "en.jpg",
"locale_used": "navigator.language = en-US (Playwright context locale en-US, fresh context)",
"measured_dimensions": "1408x964, read from file(1) on the committed bytes",
"declared_viewport_match": true,
"encoding": "real JPEG (JFIF baseline, components 3) — extension is truthful",
"bytes": 105656,
"axis_labels_readable_in_result": ["400,000", "300,000"],
"axis_labels_in_DOM_at_capture": ["0", "100,000", "200,000", "300,000", "400,000"],
"glyph_probe": "zero CJK characters anywhere on the page"
},
{
"file": "zh-Hans.jpg",
"locale_used": "navigator.language = zh-CN (Playwright context locale zh-CN, fresh context)",
"measured_dimensions": "1408x964, read from file(1) on the committed bytes",
"declared_viewport_match": true,
"encoding": "real JPEG (JFIF baseline, components 3)",
"bytes": 92604,
"axis_labels_readable_in_result": ["400,000", "300,000", "200,000"],
"axis_labels_in_DOM_at_capture": ["0", "100,000", "200,000", "300,000", "400,000"],
"glyph_probe": "localized shell present — 预览版 / 仪表盘 / 销售业绩 / 首页 / 线索 / 客户"
}
],
"pm_assumption_falsified": {
"assumption": "Zone 2 / card: the fixed English axis would read 400K / 300K and the Chinese one 40万 / 30万.",
"measured": "Both locales render 0 / 100,000 / 200,000 / 300,000 / 400,000 on sales_dashboard. The new pair is intentionally identical on the axis.",
"cause": "sales.dashboard.ts monthly_revenue_trend declares chartConfig.yAxis[0].format '0,0', so it renders grouped digits in every UI language. executive.dashboard.ts revenue_trend declares no chartConfig and takes the locale-aware compact formatter. The card measured the executive chart and attributed the behaviour to the sales chart.",
"control_probe": "Same probe, same session shape, against dashboard/executive_dashboard unchanged: en-US gives 0/100K/200K/300K/400K and zh-CN gives 0/10万/20万/30万/40万. The probe can see 万, so its absence on sales_dashboard is a property of that widget's declared format, not of the capture.",
"consequence": "The defect was real and is fixed (en.jpg no longer carries 万), but a reviewer must not read the identical axis labels as an unfixed bug. meta.yaml now carries this chain."
},
"blast_radius_remeasured": {
"card_claim": "No content/docs page references this set.",
"verdict": "Still true. Referenced sets are now customer-360, executive-dashboard, lead-detail, sales-pipeline — the card listed three; executive-dashboard was added by #1799.",
"card_conclusion_wrong": "The card concluded 'nothing is publishing the wrong axis to a reader today'. Repository root README.md line 44 renders this exact file. The wrong axis has been on the repo front page the whole time. Work unchanged (same path, same extension, replaced in place), but the fix is worth more than the card scored it."
},
"extension_decision": {
"chosen": "JPEG kept",
"verified": "file(1) on both committed files reports JPEG JFIF baseline 1408x964 — captured as JPEG, not PNG renamed",
"why": "assets/screenshots/README.md requires the real extension; this is a replacement of an existing set rather than a new set; and two consumers key on the extension — README.md line 44 and the hardcoded roster entry { id: 'sales-dashboard', extension: 'jpg' } in scripts/sync-docs-screenshots.mjs, which is the known silent-failure surface. Converting to PNG would require editing both for no gain."
},
"sync_roster_check": "Not trusted to its success message. Removed the destination dir, re-ran scripts/sync-docs-screenshots.mjs (exit 0, printed 'Synced 18'), then compared sha256: en.jpg src=95f889815f231dbc dst=95f889815f231dbc MATCH; zh-Hans.jpg src=0cea95cb25b5175e dst=0cea95cb25b5175e MATCH. Roster itself unchanged — the id was already registered.",
"gates": {
"command": "pnpm verify",
"exit_code": 0,
"exit_code_capture": "captured into a variable before any pipe; log redirected first, then tailed",
"verdict_lines": [
"✓ Validation passed (1278ms)",
"typecheck (tsc --noEmit) — no output, exit 0",
"lint — exit 0",
"✓ i18n lint gate: 0 i18n/missing-* issues (13 total lint issue(s) reported, unaffected by this gate)",
"✓ source hygiene clean",
"✓ source token ratchet clean",
"✓ Build complete (1491ms)",
"Test Files 165 passed (165) / Tests 3453 passed | 1 skipped (3454)"
],
"sha_verified_at": "6c9cbcf",
"ci": "in_progress — not waited on; report is not deferred for CI convergence"
},
"line_budget": {
"skills_surface_touched": false,
"note": "No skills/** in this repo's diff, so no line ratchet applies. The repo's own token ratchet ran green and is unchanged: business semantics ~84,609 (ceiling ~100,000), interaction layer ~37,837 (ceiling ~40,000), authored total ~137,092 (ceiling ~140,000). Nothing under src/ moved, so no token was spent."
},
"deviations": [
"Framing: captured at scroll 0 in the declared 1408x964 viewport, matching the existing pair. Since the 2026-08-01 capture the dashboard gained a Win Rate / Deals Won / Deals Lost KPI row, so the revenue-trend panel now sits lower and only its top two or three gridlines are in frame. I did NOT introduce a scroll offset or a taller viewport — either would be a second, undeclared change to the asset's framing. Seat's call if the whole chart should be in frame.",
"meta.yaml gained capture-context keys dashboard: sales_dashboard and navigation: nav_sales_dashboard, following the shape #1799 established, plus a comment recording the measured formatter chain and the per-file browser locale. Nothing reads or validates meta.yaml (git grep across scripts, test, .github, content returns no reader), so this is documentation for humans and carries no gate risk.",
"Demo data moved: seeded values differ from the 2026-08-01 capture (Total Pipeline 1,875,000 vs 3,410,000). Expected on a fresh boot; #1799 did the same.",
"A 9+ notification badge shows in the header on both frames — real app state on seeded data, not an artifact.",
"Channel switch, declared: REST /search/issues returns HTTP 403 in this container (repo-scoped REST reads work, HTTP 200), so the duplicate check used one targeted MCP search_issues call instead."
],
"landing": "Left to the seat as instructed: PR is DRAFT, NOT marked ready, auto-merge NOT armed, no labels applied. Confirmed by read-back: draft=true, state=open, base=main.",
"tests": "pnpm verify exit 0 at 6c9cbcf (verdict lines above). Capture evidence is the probe runs themselves, not a test file: two fresh-context probe runs per dashboard printing navigator.language, html lang, innerWidth/innerHeight (1408x964 both), full y-axis tick text and a CJK glyph scan. Control probe against the unchanged executive_dashboard returned 万 under zh-CN and K under en-US in the same session, which is what makes the absence of 万 on sales_dashboard a measurement rather than an assumption. Destination bytes of the docs sync verified by sha256 rather than by its 'Synced 18' message. No permanent test files added and none removed — no test in this repo references screenshots/hotcrm.",
"mcp_calls": "6 — issue_read(get), issue_read(get_comments), search_issues(dedupe, after REST 403), create_pull_request, issue_write(create #1822), add_issue_comment(this report). Issue and PR read-backs used REST, not MCP.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #1822: dashboard globalFilters labels and chartConfig axis titles have no translation surface — Sales Rep / Deal Type / Revenue render English under zh-CN, visible in the zh-Hans asset (old and new alike). Deduped first; no open card covered it. Same shape as closed #661, which routed upstream:objectstack. Filed unlabelled.",
"noted, not filed: the previous en.jpg carries a large blank band between the page header and the filter bar that the new capture does not, suggesting a widget failed to render or was still loading in the 2026-08-01 session. Successor: the asset it describes is now replaced, so nothing carries it forward — recorded only so the visual diff's extra content is not mistaken for a layout change I introduced.",
"noted, not filed: README.md line 46 prose says screenshots live at assets/screenshots/hotcrm/screen/locale.png, which the .jpg this set uses contradicts. Pre-existing, cosmetic, and touching README.md is outside the dispatched file surface. Successor: whoever next edits that README paragraph."
],
"cleanup": "Dev server stopped by recorded PID only (never by name): PID 18570 was the launcher shell, so the surviving children were located by /proc cwd matching the worktree path and terminated individually (18762, 18585, 18573, 18774). Zero survivors, port 4055 closed. node_modules removed, worktree removed without --force (exit 0), path gone."
}
✅ ACCEPT — PR #1821. Reviewed by looking at all three images, not by reading the report.
repo:hotcrm seat · session session_017FzrA1G4U89KEMf7wfLmqq · R58 review, 2026-09-09T13:2xZ. An image card cannot be reviewed from a JSON field, so the seat opened the old asset and both new ones.
What the seat saw with its own eyes
frame
reading
en.jpg (old, on main)
English sidebar, KPI tiles, funnel stages — and a y-axis reading 38万 / 28.5万 / 19万 / 9.5万. ⭐ Defect confirmed independently. Also visible: the large blank band between the page header and the filter bar that the dev noted — a widget that failed to render in the 2026-08-01 session
en.jpg (new)
zero CJK anywhere. Axis reads 400,000 / 300,000 in grouped digits. The blank band is gone, replaced by a real Win Rate / Deals Won / Deals Lost row
Both files: real JPEG, 1408x964 — verified by file(1) on the committed bytes by this seat, matching meta.yaml's declared viewport. Extension truthful.
⭐⭐ The card's own prediction was falsified, and the control is what makes that credible
The card predicted the fix would give 400K / 300K in English and 40万 / 30万 in Chinese. It gives grouped digits in both. Cause: sales.dashboard.ts's monthly_revenue_trend declares chartConfig.yAxis[0].format: '0,0', so it renders the same in every UI language; the executive dashboard's revenue_trend declares no chartConfig and takes the locale-aware compact formatter. ⇒ the card measured the executive chart and attributed its behaviour to the sales chart.
⭐ The dev proved its probe could see 万 by running it against the unchanged executive dashboard in the same session (en-US → 400K, zh-CN → 40万). ⛔ Without that control, "no 万 on the new asset" would be indistinguishable from a broken probe. That is the difference between a measurement and an absence, and it is exactly what this lane keeps asking for.
⇒ ⚠️The identical axis labels on the new pair are the correct result, not an unfixed bug.meta.yaml now records the formatter chain so the next reader does not "restore" a Chinese axis onto the English asset. ⭐ That note is the durable part of this card.
⭐ The card under-scored its own blast radius — verified
The card concluded "nothing is publishing the wrong axis to a reader today", scoped to content/docs. Measured by this seat: README.md:44 renders assets/screenshots/hotcrm/sales-dashboard/en.jpg directly. The Chinese axis has been on the repository's front page the entire time. Work unchanged — same path, same extension, replaced in place — but the fix is worth materially more than the card scored it.
Ruling on the declared framing deviation — accept as captured
The dev flagged that since the 2026-08-01 capture the dashboard gained a KPI row, so the revenue-trend panel sits lower and only its top gridlines are in frame; it explicitly declined to add a scroll offset or a taller viewport, calling either a second undeclared change. Correct, and accepted.
The asset shows what the app actually renders at the declared viewport. Introducing a scroll offset would make the frame something no user sees.
A taller viewport would require changing meta.yaml's viewport, making this the only one of eight sets with a different value — a set-wide convention change smuggled inside a locale fix. ⛔ Not this card's to make.
And the comparison that matters is against what it replaces: the old frame was wrong (Chinese axis) and carried a broken blank band. New-but-cropped beats old-but-false.
⇒ the framing question is real and is filed separately rather than folded in.
Checklist
item
reading
changed files
4: both locale files, meta.yaml, new changeset — exactly the dispatched fence ✅
viewport
measured 1408x964 both, matching the declaration ✅
extension
JPEG kept, verified as real JPEG bytes, with two consumers keying on it (README.md:44, the extension: 'jpg' roster entry) ✅
sync roster
⭐ not trusted to its success message — destination removed, re-synced, and sha256 compared src↔dst for both files: MATCH. That is the right treatment of a surface known to exit 0 while dropping a set ✅
blast radius
re-measured; card's list was stale (executive-dashboard added by #1799) ✅
CI
7/7 workflow runs on head 6c9cbcf completed / success ✅
draft held
⭐ yes — flip reserved to the seat, and honoured ✅
cleanup
dev server stopped by recorded PID, children located via /proc cwd, never by name ✅ — the lane's kill discipline, followed exactly
Landing
Not a governed path ⇒ ready + the queue, flipped by the seat after this review. ⛔ Never merged by hand.
In the ACCEPT I wrote that the framing question "is real and is filed separately rather than folded in." ⛔ I am not filing it, because on re-applying this lane's own filing bar it does not clear it.
The three classes a card must fall in are: (a) a reproducible defect, (b) a violation of a declared contract, (c) an AI-metadata trap. The framing observation is none of them:
The asset accurately shows what the app renders at the declared 1408x964 viewport. There is no defect — the page gained a KPI row, so the charts sit lower. A screenshot showing what is actually at that viewport is doing its job.
No contract says a screenshot must contain a whole chart; assets/screenshots/README.md declares the extension rule, not a composition rule.
Nothing about it misleads an authoring agent.
⇒ what I actually had was a preference about capture composition, and dressing a preference as a card is how a backlog fills with work nobody can close. ⛔ Filing it would also have implied the fix is this set's, when any real change (a taller viewport) is a convention decision across all eight sets — which is precisely why I refused to let it into this card in the first place. It cannot be both "too big for this card" and "small enough to file as its own defect" without someone stating what the defect is, and I cannot.
⭐ What is true and worth carrying: the current framing is the honest one, and if a future card ever changes the screenshot viewport, that card owns the whole set, not this one. That belongs in the acceptance record — which is here — ⛔ not in the queue.
Nothing else in the ACCEPT changes. The verdict stands, PR #1821 is ready with auto-merge armed, and the two findings that do clear the bar — #1822 (filed by the dev, graded pm:queue + Bug) and the corrected blast radius (README.md:44) — are unaffected.
Activity
huangyiirene commentedon Sep 8, 2026
First-touch grading →
pm:queue+documentation+i18n, typeTaskrepo:hotcrmseat · sessionsession_01PpRjGNnwyo2J1rrmekxB1W· R57. Single-lane repo ⇒ this seat self-serves grading. ⛔ Not dispatched this round — the shift is closing; this is graded and queued for the next one.⭐ Filed with the discriminating measurement already done. The easy version of this finding is "the English screenshot has Chinese numbers, the formatter must be broken". The dev checked and it is not: the same chart renders
400K/300K/200K/100Kunderen-USand40万/30万/20万/10万underzh-CNin its own 17.3.0 captures. ⇒ the formatter follows the UI language correctly and the asset is what is wrong — a published English capture carrying a Simplified axis.⛔ That distinction is the whole card. Without it this reads as a product defect and routes upstream; with it, it is a stale asset and lands here in one recapture.
Why it queues rather than being shrugged off
Same defect class as #981 and #1397: the lane charter's 「应用即展品…展品撒谎等于教每个照抄的 AI 撒谎」. An English-locale screenshot showing Chinese axis labels is the exhibit lying about what the app does in English — and it is on the sales dashboard, one of the more-viewed sets.
navigator.language. ⭐ That PR'sassets/screenshots/hotcrm/executive-dashboard/meta.yamlis the model to copy.sales-dashboardis the one set of eight using.jpgrather than.png, and declared viewports vary across sets (1440×1024 ×4, 1600×900 ×3, 1408×964 ×1). ⛔ Match the set's own existing shape rather than the plurality when re-capturing an existing set — this is a replacement, not a new set.Size and fence
Size S–M: one asset recaptured, in one locale, plus whatever
meta.yamlcorrection the set needs. ⛔test/**not required — no test referencesscreenshots/hotcrm(measured on #1397). ⛔ Nothing undersrc/moves; the product is correct.Generated by Claude Code
zhuangjianguo commentedon Sep 9, 2026
Claim: PM loop round R58⚠️ #1820 holds
Session:
session_017FzrA1G4U89KEMf7wfLmqqBranch:
claude/issue-1800-recapture-sales-dashboard-localesWorktree:
hotcrm-issue-1800Domain:
repo:hotcrm(single-lane repo — ⛔ nodomain:*)File surface:
assets/screenshots/hotcrm/sales-dashboard/(both locale files +meta.yaml),.changeset/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default judgement tier— ⛔ not the mechanical floor: this needs a booted app, a browser driven per-locale, and a judgement about the file extensionClause-②: no
Thread-read: the single existing comment on this card is the newest
Serial constraints cleared:
assets/screenshots/**— no in-flight card, no queued card touches it. PR #1799 (#1397) is MERGED (d01d27b3), so the Executive Overview set it added is history, not a conflict. ⭐ In flight beside you: none — you are the only dev this seat has running.AGENTS.md(governed, awaiting the maintainer) — ⛔ do not touch that file.Dispatched to a dev, who inherits this claim — ⛔ the dev posts no second claim and does not touch the assignee.
What is wrong
assets/screenshots/hotcrm/sales-dashboard/en.jpgis the published English capture, and its Monthly Revenue Trend y-axis reads38万 / 28.5万 / 19万 / 9.5万— character-for-character identical to thezh-Hans.jpglabels. Everything else on the English asset is correctly English. The asset'smeta.yamlsaysstatus: published, i.e. cleared for README and docs use.⭐ The product is not at fault and the card proves it: on the pinned 17.3.0, the same class of chart driven with
navigator.language: en-USrenders400K / 300K / 200K / 100K / 0, and withzh-CNrenders40万 / 30万 / 20万 / 10万 / 0. Both captures are in merged PR #1799. ⇒ the formatter follows the UI language; the asset was captured in the wrong locale or predates a fix.Zone 1 — the work
Re-capture both locale files of the
sales-dashboardset from a booted app, each with the browser locale set correctly for that file, and refresh the colocatedmeta.yaml(captured:date). ⛔ Do not hand-edit an image.Zone 2 — PM mechanism assumptions (⚠️ must be tested)
en.jpgyourself and read the axis. If the published asset is already correct, stop and report — the card would be stale and re-capturing would be churn. The card says it is visible at normal zoom, ⛔ but that is its reading, not mine.viewport: 1408x964is declared inmeta.yamland both current files measure exactly that — so the declaration is honest today. Use the same viewport, and verify your new files match it rather than assuming the capture tool obeyed. A silently different viewport makes the declaration false, which is the same defect class as the one you are fixing..jpgrather than.png.assets/screenshots/README.mdsays to keep the real extension. ⇒ decide deliberately and say why — if you capture PNG and save as.jpg, the extension lies; if you keep JPEG, say that was chosen. ⛔ Do not let the tool decide this by default.content/docspage references this set (the card measured: the referenced sets arecustomer-360,lead-detail,sales-pipeline). Re-measure — if a page does reference it now, the blast radius changed and that is worth reporting.assets/screenshots/hotcrm/, so the dashboard Home really opens can never be pictured in the docs #1397 showedscripts/sync-docs-screenshots.mjsexiting 0 and printing "Synced 16" with the set absent. ⇒ if you touch anything the sync reads, ⛔ do not treat a success message and a plausible count as evidence.Zone 3 — suggested route (⛔ optional, measurement beats it)
The
dogfood-verificationskill covers booting a real app and driving it in a browser; PR #1799 is the worked precedent for this exact set of conventions and is worth reading first. Capture English and Chinese in the same session shape so the only difference between the two files is the locale — that is what makes the pair comparable, and it is the thing the currenten.jpgfailed at.Landing
Not a governed path (⚠️ But leave the flip to the seat: report when green, ⛔ do not mark ready and ⛔ do not arm auto-merge. This lane learned today that arming on a PR whose checks have already finished merges it immediately, leaving no review window — and an image diff is exactly the kind a reviewer must actually look at.
assets/**,.changeset/) ⇒ eligible for ready + the queue.⭐ Landing fact, corrected this round: this repo does have a merge queue (ruleset
12187346), and arming enqueues; the queue merges within seconds. ⛔ Seat-post §5.1's "no merge queue" is false.Report
Terminal report to this issue first, then as the return message. Machine-checkable fields only —
gates/line_budget/deviations/files_changed. Additionally state, per file: the locale used, the measured pixel dimensions, and the axis labels you can read in the result — those are the machine-checkable facts for an image card, and "it looks right" is not one of them.Generated by Claude Code
zhuangjianguo commentedon Sep 9, 2026
os-dev-report
{ "issue": 1800, "status": "done", "branch": "claude/issue-1800-recapture-sales-dashboard-locales", "pr": "https://github.com/objectstack-ai/hotcrm/pull/1821", "premise_still_valid": true, "premise_evidence": "Opened en.jpg and read the axis directly rather than inheriting the card's reading: English sidebar, KPI tiles, funnel stages and metric sub-labels, with a y-axis reading 38万 / 28.5万 / 19万 / 9.5万. Defect confirmed. PR is not draft-only on a dead premise.", "summary": "Re-captured both locale files of assets/screenshots/hotcrm/sales-dashboard/ from a booted app (port 4055, seeded demo data) at the id route /_console/apps/app.objectstack.hotcrm/dashboard/sales_dashboard, in two fresh browser contexts differing only in navigator.language. Refreshed meta.yaml captured: to 2026-09-09, added the capture-context keys #1799 established, and recorded the measured formatter chain in a comment. Neither image was hand-edited. One PM mechanism assumption was falsified by measurement and is reported below.", "files_changed": [ "assets/screenshots/hotcrm/sales-dashboard/en.jpg", "assets/screenshots/hotcrm/sales-dashboard/zh-Hans.jpg", "assets/screenshots/hotcrm/sales-dashboard/meta.yaml", ".changeset/sales-dashboard-recaptured-in-its-own-locale.md" ], "file_surface_breach": "none — exactly the dispatched fence (the sales-dashboard set plus .changeset/)", "per_file": [ { "file": "en.jpg", "locale_used": "navigator.language = en-US (Playwright context locale en-US, fresh context)", "measured_dimensions": "1408x964, read from file(1) on the committed bytes", "declared_viewport_match": true, "encoding": "real JPEG (JFIF baseline, components 3) — extension is truthful", "bytes": 105656, "axis_labels_readable_in_result": ["400,000", "300,000"], "axis_labels_in_DOM_at_capture": ["0", "100,000", "200,000", "300,000", "400,000"], "glyph_probe": "zero CJK characters anywhere on the page" }, { "file": "zh-Hans.jpg", "locale_used": "navigator.language = zh-CN (Playwright context locale zh-CN, fresh context)", "measured_dimensions": "1408x964, read from file(1) on the committed bytes", "declared_viewport_match": true, "encoding": "real JPEG (JFIF baseline, components 3)", "bytes": 92604, "axis_labels_readable_in_result": ["400,000", "300,000", "200,000"], "axis_labels_in_DOM_at_capture": ["0", "100,000", "200,000", "300,000", "400,000"], "glyph_probe": "localized shell present — 预览版 / 仪表盘 / 销售业绩 / 首页 / 线索 / 客户" } ], "pm_assumption_falsified": { "assumption": "Zone 2 / card: the fixed English axis would read 400K / 300K and the Chinese one 40万 / 30万.", "measured": "Both locales render 0 / 100,000 / 200,000 / 300,000 / 400,000 on sales_dashboard. The new pair is intentionally identical on the axis.", "cause": "sales.dashboard.ts monthly_revenue_trend declares chartConfig.yAxis[0].format '0,0', so it renders grouped digits in every UI language. executive.dashboard.ts revenue_trend declares no chartConfig and takes the locale-aware compact formatter. The card measured the executive chart and attributed the behaviour to the sales chart.", "control_probe": "Same probe, same session shape, against dashboard/executive_dashboard unchanged: en-US gives 0/100K/200K/300K/400K and zh-CN gives 0/10万/20万/30万/40万. The probe can see 万, so its absence on sales_dashboard is a property of that widget's declared format, not of the capture.", "consequence": "The defect was real and is fixed (en.jpg no longer carries 万), but a reviewer must not read the identical axis labels as an unfixed bug. meta.yaml now carries this chain." }, "blast_radius_remeasured": { "card_claim": "No content/docs page references this set.", "verdict": "Still true. Referenced sets are now customer-360, executive-dashboard, lead-detail, sales-pipeline — the card listed three; executive-dashboard was added by #1799.", "card_conclusion_wrong": "The card concluded 'nothing is publishing the wrong axis to a reader today'. Repository root README.md line 44 renders this exact file. The wrong axis has been on the repo front page the whole time. Work unchanged (same path, same extension, replaced in place), but the fix is worth more than the card scored it." }, "extension_decision": { "chosen": "JPEG kept", "verified": "file(1) on both committed files reports JPEG JFIF baseline 1408x964 — captured as JPEG, not PNG renamed", "why": "assets/screenshots/README.md requires the real extension; this is a replacement of an existing set rather than a new set; and two consumers key on the extension — README.md line 44 and the hardcoded roster entry { id: 'sales-dashboard', extension: 'jpg' } in scripts/sync-docs-screenshots.mjs, which is the known silent-failure surface. Converting to PNG would require editing both for no gain." }, "sync_roster_check": "Not trusted to its success message. Removed the destination dir, re-ran scripts/sync-docs-screenshots.mjs (exit 0, printed 'Synced 18'), then compared sha256: en.jpg src=95f889815f231dbc dst=95f889815f231dbc MATCH; zh-Hans.jpg src=0cea95cb25b5175e dst=0cea95cb25b5175e MATCH. Roster itself unchanged — the id was already registered.", "gates": { "command": "pnpm verify", "exit_code": 0, "exit_code_capture": "captured into a variable before any pipe; log redirected first, then tailed", "verdict_lines": [ "✓ Validation passed (1278ms)", "typecheck (tsc --noEmit) — no output, exit 0", "lint — exit 0", "✓ i18n lint gate: 0 i18n/missing-* issues (13 total lint issue(s) reported, unaffected by this gate)", "✓ source hygiene clean", "✓ source token ratchet clean", "✓ Build complete (1491ms)", "Test Files 165 passed (165) / Tests 3453 passed | 1 skipped (3454)" ], "sha_verified_at": "6c9cbcf", "ci": "in_progress — not waited on; report is not deferred for CI convergence" }, "line_budget": { "skills_surface_touched": false, "note": "No skills/** in this repo's diff, so no line ratchet applies. The repo's own token ratchet ran green and is unchanged: business semantics ~84,609 (ceiling ~100,000), interaction layer ~37,837 (ceiling ~40,000), authored total ~137,092 (ceiling ~140,000). Nothing under src/ moved, so no token was spent." }, "deviations": [ "Framing: captured at scroll 0 in the declared 1408x964 viewport, matching the existing pair. Since the 2026-08-01 capture the dashboard gained a Win Rate / Deals Won / Deals Lost KPI row, so the revenue-trend panel now sits lower and only its top two or three gridlines are in frame. I did NOT introduce a scroll offset or a taller viewport — either would be a second, undeclared change to the asset's framing. Seat's call if the whole chart should be in frame.", "meta.yaml gained capture-context keys dashboard: sales_dashboard and navigation: nav_sales_dashboard, following the shape #1799 established, plus a comment recording the measured formatter chain and the per-file browser locale. Nothing reads or validates meta.yaml (git grep across scripts, test, .github, content returns no reader), so this is documentation for humans and carries no gate risk.", "Demo data moved: seeded values differ from the 2026-08-01 capture (Total Pipeline 1,875,000 vs 3,410,000). Expected on a fresh boot; #1799 did the same.", "A 9+ notification badge shows in the header on both frames — real app state on seeded data, not an artifact.", "Channel switch, declared: REST /search/issues returns HTTP 403 in this container (repo-scoped REST reads work, HTTP 200), so the duplicate check used one targeted MCP search_issues call instead." ], "landing": "Left to the seat as instructed: PR is DRAFT, NOT marked ready, auto-merge NOT armed, no labels applied. Confirmed by read-back: draft=true, state=open, base=main.", "tests": "pnpm verify exit 0 at 6c9cbcf (verdict lines above). Capture evidence is the probe runs themselves, not a test file: two fresh-context probe runs per dashboard printing navigator.language, html lang, innerWidth/innerHeight (1408x964 both), full y-axis tick text and a CJK glyph scan. Control probe against the unchanged executive_dashboard returned 万 under zh-CN and K under en-US in the same session, which is what makes the absence of 万 on sales_dashboard a measurement rather than an assumption. Destination bytes of the docs sync verified by sha256 rather than by its 'Synced 18' message. No permanent test files added and none removed — no test in this repo references screenshots/hotcrm.", "mcp_calls": "6 — issue_read(get), issue_read(get_comments), search_issues(dedupe, after REST 403), create_pull_request, issue_write(create #1822), add_issue_comment(this report). Issue and PR read-backs used REST, not MCP.", "open_questions": [], "out_of_scope_findings": [ "filed as #1822: dashboard globalFilters labels and chartConfig axis titles have no translation surface — Sales Rep / Deal Type / Revenue render English under zh-CN, visible in the zh-Hans asset (old and new alike). Deduped first; no open card covered it. Same shape as closed #661, which routed upstream:objectstack. Filed unlabelled.", "noted, not filed: the previous en.jpg carries a large blank band between the page header and the filter bar that the new capture does not, suggesting a widget failed to render or was still loading in the 2026-08-01 session. Successor: the asset it describes is now replaced, so nothing carries it forward — recorded only so the visual diff's extra content is not mistaken for a layout change I introduced.", "noted, not filed: README.md line 46 prose says screenshots live at assets/screenshots/hotcrm/screen/locale.png, which the .jpg this set uses contradicts. Pre-existing, cosmetic, and touching README.md is outside the dispatched file surface. Successor: whoever next edits that README paragraph." ], "cleanup": "Dev server stopped by recorded PID only (never by name): PID 18570 was the launcher shell, so the surviving children were located by /proc cwd matching the worktree path and terminated individually (18762, 18585, 18573, 18774). Zero survivors, port 4055 closed. node_modules removed, worktree removed without --force (exit 0), path gone." }Generated by Claude Code
Generated by Claude Code
zhuangjianguo commentedon Sep 9, 2026
✅ ACCEPT — PR #1821. Reviewed by looking at all three images, not by reading the report.
repo:hotcrmseat · sessionsession_017FzrA1G4U89KEMf7wfLmqq· R58 review, 2026-09-09T13:2xZ. An image card cannot be reviewed from a JSON field, so the seat opened the old asset and both new ones.What the seat saw with its own eyes
en.jpg(old, onmain)38万 / 28.5万 / 19万 / 9.5万. ⭐ Defect confirmed independently. Also visible: the large blank band between the page header and the filter bar that the dev noted — a widget that failed to render in the 2026-08-01 sessionen.jpg(new)400,000/300,000in grouped digits. The blank band is gone, replaced by a real Win Rate / Deals Won / Deals Lost rowzh-Hans.jpg(new)Both files: real JPEG,
1408x964— verified byfile(1)on the committed bytes by this seat, matchingmeta.yaml's declared viewport. Extension truthful.⭐⭐ The card's own prediction was falsified, and the control is what makes that credible
The card predicted the fix would give
400K / 300Kin English and40万 / 30万in Chinese. It gives grouped digits in both. Cause:sales.dashboard.ts'smonthly_revenue_trenddeclareschartConfig.yAxis[0].format: '0,0', so it renders the same in every UI language; the executive dashboard'srevenue_trenddeclares nochartConfigand takes the locale-aware compact formatter. ⇒ the card measured the executive chart and attributed its behaviour to the sales chart.⭐ The dev proved its probe could see
万by running it against the unchanged executive dashboard in the same session (en-US→400K,zh-CN→40万). ⛔ Without that control, "no 万 on the new asset" would be indistinguishable from a broken probe. That is the difference between a measurement and an absence, and it is exactly what this lane keeps asking for.⇒⚠️ The identical axis labels on the new pair are the correct result, not an unfixed bug.
meta.yamlnow records the formatter chain so the next reader does not "restore" a Chinese axis onto the English asset. ⭐ That note is the durable part of this card.⭐ The card under-scored its own blast radius — verified
The card concluded "nothing is publishing the wrong axis to a reader today", scoped to
content/docs. Measured by this seat:README.md:44rendersassets/screenshots/hotcrm/sales-dashboard/en.jpgdirectly. The Chinese axis has been on the repository's front page the entire time. Work unchanged — same path, same extension, replaced in place — but the fix is worth materially more than the card scored it.Ruling on the declared framing deviation — accept as captured
The dev flagged that since the 2026-08-01 capture the dashboard gained a KPI row, so the revenue-trend panel sits lower and only its top gridlines are in frame; it explicitly declined to add a scroll offset or a taller viewport, calling either a second undeclared change. Correct, and accepted.
meta.yaml'sviewport, making this the only one of eight sets with a different value — a set-wide convention change smuggled inside a locale fix. ⛔ Not this card's to make.⇒ the framing question is real and is filed separately rather than folded in.
Checklist
meta.yaml, new changeset — exactly the dispatched fence ✅1408x964both, matching the declaration ✅README.md:44, theextension: 'jpg'roster entry) ✅6c9cbcfcompleted / success ✅/proccwd, never by name ✅ — the lane's kill discipline, followed exactlyLanding
Not a governed path ⇒ ready + the queue, flipped by the seat after this review. ⛔ Never merged by hand.
Generated by Claude Code
zhuangjianguo commentedon Sep 9, 2026
⛔ Correction to my own ACCEPT above — the framing observation is not being filed, and saying it would be was the wrong call
repo:hotcrmseat · sessionsession_017FzrA1G4U89KEMf7wfLmqq· R58, 2026-09-09T13:2xZ.In the ACCEPT I wrote that the framing question "is real and is filed separately rather than folded in." ⛔ I am not filing it, because on re-applying this lane's own filing bar it does not clear it.
The three classes a card must fall in are: (a) a reproducible defect, (b) a violation of a declared contract, (c) an AI-metadata trap. The framing observation is none of them:
1408x964viewport. There is no defect — the page gained a KPI row, so the charts sit lower. A screenshot showing what is actually at that viewport is doing its job.assets/screenshots/README.mddeclares the extension rule, not a composition rule.⇒ what I actually had was a preference about capture composition, and dressing a preference as a card is how a backlog fills with work nobody can close. ⛔ Filing it would also have implied the fix is this set's, when any real change (a taller viewport) is a convention decision across all eight sets — which is precisely why I refused to let it into this card in the first place. It cannot be both "too big for this card" and "small enough to file as its own defect" without someone stating what the defect is, and I cannot.
⭐ What is true and worth carrying: the current framing is the honest one, and if a future card ever changes the screenshot viewport, that card owns the whole set, not this one. That belongs in the acceptance record — which is here — ⛔ not in the queue.
Nothing else in the ACCEPT changes. The verdict stands, PR #1821 is ready with auto-merge armed, and the two findings that do clear the bar — #1822 (filed by the dev, graded
pm:queue+Bug) and the corrected blast radius (README.md:44) — are unaffected.Generated by Claude Code