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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Studio 工作台把内部 ID(app.b2r4 / customer_dashboard)与整块英文(属性面板、AI 工具卡)摆给中文客户;工具卡头部状态与卡体徽章不一致 #7254
Claim: PM loop round 1 (epic objectstack-ai/cloud#1841)
Session: session_c5c0ce54-bb9c-478c-9e5b-cf44b80d4569
Branch: claude/issue-7254-studio-jargon-and-i18n
Worktree: objectui-issue-7254
Domain: domain:ui
File surface: packages/app-shell/src/console/studio/** (header/breadcrumb/property panel labels), packages/i18n/** (zh dictionaries), AI tool-card status under packages/app-shell/src/console/** (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus (PM judgement)
Clause-②: no
Serial constraints cleared: only dependabot bumps touch app-shell/i18n package.json; sibling #7255 (studio nav refresh) is SERIAL after this card; #7253 owns marketplace + quota handling — do not edit those files
Claim: dev
Session: session_c5c0ce54-bb9c-478c-9e5b-cf44b80d4569
Branch: claude/issue-7254-studio-jargon-and-i18n
Worktree: objectui-issue-7254
Base: 67dadd602a3a891666ea1513c5de677140784b6a
File surface: packages/app-shell/src/console/studio/**, packages/i18n/** (zh + en dictionaries), AI tool-card status components under packages/app-shell/src/console/**
Not touching: marketplace / quota files (#7253), metadata-events subscription hook (#7257), studio nav refresh (#7255)
{
"issue": 7254,
"status": "done",
"branch": "claude/issue-7254-studio-jargon-and-i18n",
"pr": "https://github.com/objectstack-ai/objectui/pull/7366",
"premise_still_valid": true,
"summary": "All three parts of the card landed. (1) Workbench: the Interfaces breadcrumb and canvas caption now read metadata label + translated kind with the internal `type · name` pair moved to a labelled tooltip; the nav rail's kind chip is translated and an unlabelled leaf falls back to its object name instead of rendering an empty row; `parsePackages` reads the package name from BOTH positions `GET /api/v1/packages` serves it in, which is why the top bar showed `app.b2r4` (the reader already matched both positions for `id` and only for `id`; the server's own list handler reads `item.manifest?.id || item.id` on both halves, so this closes an asymmetry rather than adding a dialect). (2) Property panel: a new `metadata-form-i18n.ts` supplies a bundle to the platform's OWN `resolveMetadataFormLabels` from `@objectstack/spec/system` — the same function the framework's `/meta/types` handler calls — so section slugging, dot-path field addressing and the `header` composite's sub-field synthesis all come from the producer. (3) Tool cards: titles resolve through `chatbot.tool.TOOL_NAME` (all 30 platform tools x 10 packs), the header status badge is localized through an additive `statusLabels` seam on the vendored element, the plan count strip is a real plural family, and `resolveProposalCardState` is now the ONE producer both the header badge and the body badge read — a confirmed/built/published proposal no longer keeps a header saying 'Awaiting Approval'. PM mechanism assumptions checked against main and both were WRONG in detail: `packages/app-shell/src/console/studio/**` does not exist (the surface is `views/studio-design/**`), the top bar is a PACKAGE switcher that already preferred `manifest.name` (the producer DOES write it — cloud `ensureAppPackage` sets `name = app.label ?? app.name` — so the defect was the consumer's one-position read, not a missing label), and the breadcrumb already rendered `current.label` with the internal pair as an ADDITIONAL chip beside it rather than instead of it. objectui#2458 read; no overlap — it is the chat card, this is the workbench plus the status single-source.",
"tests": "Run at 2d332f868 (final commit, after rebase onto ab9294039), exit codes redirected before capture. `pnpm exec vitest run packages/plugin-chatbot/ packages/i18n/ packages/app-shell/src/views/studio-design/` -> 'Test Files 136 passed (136) / Tests 1592 passed (1592)'. `pnpm exec vitest run packages/app-shell/src/views/metadata-admin/ packages/app-shell/src/preview/ packages/app-shell/src/console/ai/` -> 'Test Files 254 passed (254) / Tests 2533 passed | 1 skipped (2534)'. type-check exit 0 for i18n, plugin-chatbot and app-shell (each runs `tsc --noEmit && tsc -p tsconfig.test.json`, so the new test files ARE covered, not excluded). build exit 0 for the same three ('Done'). Gate verdict lines, quoted from the gates: check-i18n-call-site-keys 'Every in-scope call-site key resolves against the en pack (2952 keys) ...'; check-i18n-en-drift 'No en value changed in this range.' (46 keys added, 0 changed); check-control-bytes 'OK (scanned 6048 tracked text file(s))'; check-changeset-presence '24 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major 'No changeset declares a major bump.' ABLATION (run from the committed state so the restore leg had a real reference): reverted the header-state fix on disk with `perl -0pi`, confirmed the mutation LANDED by grep counts (injected 1 / removed 0) AND a changed `git hash-object` (deac5cb3 -> 100e3152) before running anything; `toolCardHeaderState-7254.test.tsx` then went 3 failed / 4 passed — exactly the three pins asserting the header follows the body, while the unanswered-proposal pin and the four unit cases stayed green (correct: `resolveProposalCardState` still existed). Restored with `git checkout HEAD -- path`, proved by an empty `git diff HEAD`. No dist involved: the pins import `../ChatbotEnhanced` by relative source specifier. DECLARED NARROWINGS, both reported rather than skipped: (a) the whole-package app-shell vitest run exceeds the container's 10-minute foreground cap (exit 143), so the two directories this diff touches were run in full plus `preview/` and `console/ai/`, the other consumers of the changed components, found by grepping the tests for the strings this PR moves; (b) repo-wide `pnpm lint` is CI's run — eslint was run on exactly the 24 changed .ts/.tsx paths with the three required pieces of evidence: population from eslint's own config resolution, file count from its `--format json` output (24 files, 1 error, 36 warnings), and this repo enables no type-aware linting so the diff cannot move a verdict on an untouched file. Exactly ONE message lands on a line this PR wrote (`react-refresh/only-export-components` on the new `resolveProposalCardState` export — a warning, the sixth of that same family already firing in that file). The single ERROR (`react-hooks/static-components` in StudioDesignSurface.tsx) is pre-existing: linting origin/main's own copy of that file reports the identical error. NOT MEASURED / known-red elsewhere: `console/__tests__/anonSeedScope-5746.enumeration.test.tsx` fails 7/7 — and fails identically with this PR's source changes reverted to the merge base, so it is pre-existing and unrelated.",
"mcp_calls": "0 — the whole run went through git plus repo-scoped REST (`gh api`); the write-path probe (empty-branch push) and the dedupe searches both succeeded on that channel, so no MCP GitHub call was needed",
"open_questions": [
{
"question": "The ruling said the property panel's strings go through the `@object-ui/i18n` dictionary. They went through the `engine.*` carve-out beside them (`views/metadata-admin/i18n.ts`, en+zh) instead. Declaring the deviation rather than taking it silently — the tool-card half IS in `@object-ui/i18n` as directed, and the user-visible outcome the ruling demanded (full zh coverage of the listed strings) is met either way.",
"options": [
"A — keep it in the `engine.*` carve-out (what landed): every other string on that same panel already comes from there (`engine.inspector.dashboard.*`, `engine.studio.*`), so one panel is served by one dictionary; the carve-out is documented on both ends with a deliberately narrow reopening condition; and `@object-ui/i18n` enforces full parity across ten packs, so the alternative meant inventing technical form copy in eight more languages with no pull.",
"B — move it to `@object-ui/i18n` as the ruling reads literally: costs ~30 keys x 10 packs of unrequested translation and splits one panel across two dictionaries, but is one dictionary for the whole product.",
"C — drop the objectui-side table entirely and consume the platform's shipped `metadataForms` bundle over the wire (the framework ALREADY localizes `/meta/types` entries' nested `form` via `resolveMetadataFormLabels`, and objectstack already ships zh/ja/es for `dashboard`)."
],
"recommendation": "A now, C as the destination — and C is why A is not a compromise. Long-term soundness (the axis that leads): these strings are the SPEC's, so the durable answer is that the producer's own bundle reaches this surface, which is exactly what the landed shape prepares — it feeds the platform's own resolver and its own key convention, so C is later a source swap at one call site, not a rewrite. B buys nothing C does not, and pays for it in a second dictionary on one panel. Real business need: the pull is measured (a Chinese customer's rig, this card), and it is for zh — B's eight extra languages have zero measured pull. AI-authorability: A and C both keep the key scheme the platform already validates, so an author-visible label has ONE spelling; B mints a second scheme for the same strings, which is where drift lives. Startup scope: A is the smallest thing that closes the measured gap. C was not done here because the server-localized `form` is not threaded to the inspector today and the reachability of the environment's bundle in a cloud tenant could not be verified without a live rig — an unverified claim is not a fix. Filed as the open question rather than decided unilaterally, since it is a ruling the PM owns."
}
],
"out_of_scope_findings": [
"filed as objectstack#14586: spec(ui) dashboard `gap` help text says \"Tailwind units\" to an app author — the string is rendered verbatim in the customer-facing property panel; the zh overlay says something actionable and deliberately leaves the producer's English alone",
"filed as objectstack#14587: platform-objects zh-CN metadata-forms `dashboard.sections.layout.description` is stale against its source (says 「栅格与响应式」 for \"Grid sizing and refresh cadence.\"), the merge-only-fills-gaps failure mode the generated file's own header names; includes the sibling leaf and the class-level question about the source-hash machinery",
"filed as objectui#7368: PackageSwitcher swallows the package-list fetch failure (`.catch(() => {})`), so any 503/network/auth failure leaves the raw package id in the Studio top bar forever with no signal — and makes \"no declared name\" indistinguishable from \"the list never loaded\""
]
}
Mechanism corrections accepted as read: the top-bar defect was the consumer's one-position read of manifest.name (producer already writes it), not a missing label.
Ablation on the header-state fix proven on disk (hash-object + grep counts), 3 red / 4 green as predicted, byte-identical restore. Focused suites 136 files / 1592 + 254 files / 2533 green; type-check covers the new tests (tsconfig.test.json); gates quoted (i18n call-site keys, en-drift, control-bytes, changeset). Narrowings declared with evidence (10-min cap on the whole-package run; eslint population from its own config, 24 files).
Ruling on the open question (PM discretion — dictionary placement, not a product/contract fork): A now, C as the destination. The property-panel strings stay in the engine.* carve-out beside the rest of that panel; the destination — consume the platform-localized form/bundle and delete the objectui table — is recorded as objectui#7369 (finding), with the two unverified premises the dev named.
Activity
hotlong commentedon Sep 2, 2026
Claim: PM loop round 1 (epic objectstack-ai/cloud#1841)
Session:
session_c5c0ce54-bb9c-478c-9e5b-cf44b80d4569Branch:
claude/issue-7254-studio-jargon-and-i18nWorktree:
objectui-issue-7254Domain: domain:ui
File surface:
packages/app-shell/src/console/studio/**(header/breadcrumb/property panel labels),packages/i18n/**(zh dictionaries), AI tool-card status underpackages/app-shell/src/console/**(stop on breach; explain in the report)Container & model: M, mode:subagent, model: opus (PM judgement)
Clause-②: no
Serial constraints cleared: only dependabot bumps touch app-shell/i18n package.json; sibling #7255 (studio nav refresh) is SERIAL after this card; #7253 owns marketplace + quota handling — do not edit those files
hotlong commentedon Sep 2, 2026
Claim: dev
Session:
session_c5c0ce54-bb9c-478c-9e5b-cf44b80d4569Branch:
claude/issue-7254-studio-jargon-and-i18nWorktree:
objectui-issue-7254Base:
67dadd602a3a891666ea1513c5de677140784b6aFile surface:
packages/app-shell/src/console/studio/**,packages/i18n/**(zh + en dictionaries), AI tool-card status components underpackages/app-shell/src/console/**Not touching: marketplace / quota files (#7253), metadata-events subscription hook (#7257), studio nav refresh (#7255)
Generated by Claude Code
fix(studio,chatbot,i18n): workbench labels over internal ids, localiz…
fix(studio): name the internal-id tooltip instead of repeating the ba…
test(metadata-admin): type the form shapes the overlay pins read
hotlong commentedon Sep 2, 2026
os-dev-report
{ "issue": 7254, "status": "done", "branch": "claude/issue-7254-studio-jargon-and-i18n", "pr": "https://github.com/objectstack-ai/objectui/pull/7366", "premise_still_valid": true, "summary": "All three parts of the card landed. (1) Workbench: the Interfaces breadcrumb and canvas caption now read metadata label + translated kind with the internal `type · name` pair moved to a labelled tooltip; the nav rail's kind chip is translated and an unlabelled leaf falls back to its object name instead of rendering an empty row; `parsePackages` reads the package name from BOTH positions `GET /api/v1/packages` serves it in, which is why the top bar showed `app.b2r4` (the reader already matched both positions for `id` and only for `id`; the server's own list handler reads `item.manifest?.id || item.id` on both halves, so this closes an asymmetry rather than adding a dialect). (2) Property panel: a new `metadata-form-i18n.ts` supplies a bundle to the platform's OWN `resolveMetadataFormLabels` from `@objectstack/spec/system` — the same function the framework's `/meta/types` handler calls — so section slugging, dot-path field addressing and the `header` composite's sub-field synthesis all come from the producer. (3) Tool cards: titles resolve through `chatbot.tool.TOOL_NAME` (all 30 platform tools x 10 packs), the header status badge is localized through an additive `statusLabels` seam on the vendored element, the plan count strip is a real plural family, and `resolveProposalCardState` is now the ONE producer both the header badge and the body badge read — a confirmed/built/published proposal no longer keeps a header saying 'Awaiting Approval'. PM mechanism assumptions checked against main and both were WRONG in detail: `packages/app-shell/src/console/studio/**` does not exist (the surface is `views/studio-design/**`), the top bar is a PACKAGE switcher that already preferred `manifest.name` (the producer DOES write it — cloud `ensureAppPackage` sets `name = app.label ?? app.name` — so the defect was the consumer's one-position read, not a missing label), and the breadcrumb already rendered `current.label` with the internal pair as an ADDITIONAL chip beside it rather than instead of it. objectui#2458 read; no overlap — it is the chat card, this is the workbench plus the status single-source.", "tests": "Run at 2d332f868 (final commit, after rebase onto ab9294039), exit codes redirected before capture. `pnpm exec vitest run packages/plugin-chatbot/ packages/i18n/ packages/app-shell/src/views/studio-design/` -> 'Test Files 136 passed (136) / Tests 1592 passed (1592)'. `pnpm exec vitest run packages/app-shell/src/views/metadata-admin/ packages/app-shell/src/preview/ packages/app-shell/src/console/ai/` -> 'Test Files 254 passed (254) / Tests 2533 passed | 1 skipped (2534)'. type-check exit 0 for i18n, plugin-chatbot and app-shell (each runs `tsc --noEmit && tsc -p tsconfig.test.json`, so the new test files ARE covered, not excluded). build exit 0 for the same three ('Done'). Gate verdict lines, quoted from the gates: check-i18n-call-site-keys 'Every in-scope call-site key resolves against the en pack (2952 keys) ...'; check-i18n-en-drift 'No en value changed in this range.' (46 keys added, 0 changed); check-control-bytes 'OK (scanned 6048 tracked text file(s))'; check-changeset-presence '24 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major 'No changeset declares a major bump.' ABLATION (run from the committed state so the restore leg had a real reference): reverted the header-state fix on disk with `perl -0pi`, confirmed the mutation LANDED by grep counts (injected 1 / removed 0) AND a changed `git hash-object` (deac5cb3 -> 100e3152) before running anything; `toolCardHeaderState-7254.test.tsx` then went 3 failed / 4 passed — exactly the three pins asserting the header follows the body, while the unanswered-proposal pin and the four unit cases stayed green (correct: `resolveProposalCardState` still existed). Restored with `git checkout HEAD -- path`, proved by an empty `git diff HEAD`. No dist involved: the pins import `../ChatbotEnhanced` by relative source specifier. DECLARED NARROWINGS, both reported rather than skipped: (a) the whole-package app-shell vitest run exceeds the container's 10-minute foreground cap (exit 143), so the two directories this diff touches were run in full plus `preview/` and `console/ai/`, the other consumers of the changed components, found by grepping the tests for the strings this PR moves; (b) repo-wide `pnpm lint` is CI's run — eslint was run on exactly the 24 changed .ts/.tsx paths with the three required pieces of evidence: population from eslint's own config resolution, file count from its `--format json` output (24 files, 1 error, 36 warnings), and this repo enables no type-aware linting so the diff cannot move a verdict on an untouched file. Exactly ONE message lands on a line this PR wrote (`react-refresh/only-export-components` on the new `resolveProposalCardState` export — a warning, the sixth of that same family already firing in that file). The single ERROR (`react-hooks/static-components` in StudioDesignSurface.tsx) is pre-existing: linting origin/main's own copy of that file reports the identical error. NOT MEASURED / known-red elsewhere: `console/__tests__/anonSeedScope-5746.enumeration.test.tsx` fails 7/7 — and fails identically with this PR's source changes reverted to the merge base, so it is pre-existing and unrelated.", "mcp_calls": "0 — the whole run went through git plus repo-scoped REST (`gh api`); the write-path probe (empty-branch push) and the dedupe searches both succeeded on that channel, so no MCP GitHub call was needed", "open_questions": [ { "question": "The ruling said the property panel's strings go through the `@object-ui/i18n` dictionary. They went through the `engine.*` carve-out beside them (`views/metadata-admin/i18n.ts`, en+zh) instead. Declaring the deviation rather than taking it silently — the tool-card half IS in `@object-ui/i18n` as directed, and the user-visible outcome the ruling demanded (full zh coverage of the listed strings) is met either way.", "options": [ "A — keep it in the `engine.*` carve-out (what landed): every other string on that same panel already comes from there (`engine.inspector.dashboard.*`, `engine.studio.*`), so one panel is served by one dictionary; the carve-out is documented on both ends with a deliberately narrow reopening condition; and `@object-ui/i18n` enforces full parity across ten packs, so the alternative meant inventing technical form copy in eight more languages with no pull.", "B — move it to `@object-ui/i18n` as the ruling reads literally: costs ~30 keys x 10 packs of unrequested translation and splits one panel across two dictionaries, but is one dictionary for the whole product.", "C — drop the objectui-side table entirely and consume the platform's shipped `metadataForms` bundle over the wire (the framework ALREADY localizes `/meta/types` entries' nested `form` via `resolveMetadataFormLabels`, and objectstack already ships zh/ja/es for `dashboard`)." ], "recommendation": "A now, C as the destination — and C is why A is not a compromise. Long-term soundness (the axis that leads): these strings are the SPEC's, so the durable answer is that the producer's own bundle reaches this surface, which is exactly what the landed shape prepares — it feeds the platform's own resolver and its own key convention, so C is later a source swap at one call site, not a rewrite. B buys nothing C does not, and pays for it in a second dictionary on one panel. Real business need: the pull is measured (a Chinese customer's rig, this card), and it is for zh — B's eight extra languages have zero measured pull. AI-authorability: A and C both keep the key scheme the platform already validates, so an author-visible label has ONE spelling; B mints a second scheme for the same strings, which is where drift lives. Startup scope: A is the smallest thing that closes the measured gap. C was not done here because the server-localized `form` is not threaded to the inspector today and the reachability of the environment's bundle in a cloud tenant could not be verified without a live rig — an unverified claim is not a fix. Filed as the open question rather than decided unilaterally, since it is a ruling the PM owns." } ], "out_of_scope_findings": [ "filed as objectstack#14586: spec(ui) dashboard `gap` help text says \"Tailwind units\" to an app author — the string is rendered verbatim in the customer-facing property panel; the zh overlay says something actionable and deliberately leaves the producer's English alone", "filed as objectstack#14587: platform-objects zh-CN metadata-forms `dashboard.sections.layout.description` is stale against its source (says 「栅格与响应式」 for \"Grid sizing and refresh cadence.\"), the merge-only-fills-gaps failure mode the generated file's own header names; includes the sibling leaf and the class-level question about the source-hash machinery", "filed as objectui#7368: PackageSwitcher swallows the package-list fetch failure (`.catch(() => {})`), so any 503/network/auth failure leaves the raw package id in the Studio top bar forever with no signal — and makes \"no declared name\" indistinguishable from \"the list never loaded\"" ] }Generated by Claude Code
hotlong commentedon Sep 2, 2026
ACCEPT — reviewer of record: PM (epic objectstack-ai/cloud#1841), verified against GitHub.
Fixes #7254, 24 files:views/studio-design/**+views/metadata-admin/**(the real workbench surface — my claim named a directory that does not exist; the dev corrected it),packages/i18n(46 keys added across the 10 packs, en drift 0),plugin-chatbottool card (title/status/plural +resolveProposalCardStateas the single producer for header and body badges), changeset. All inside the intent of the claimed surface; no overlap with fix(app-shell,plugin-chatbot): repair the three breaks at the AI paywall moment #7273 (merged) after rebase.manifest.name(producer already writes it), not a missing label.engine.*carve-out beside the rest of that panel; the destination — consume the platform-localizedform/bundle and delete the objectui table — is recorded as objectui#7369 (finding), with the two unverified premises the dev named.Generated by Claude Code
fix(studio,chatbot,i18n): workbench labels over internal ids, localiz…