Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

New package form: display name first; derived id, namespace and version are replaced on edit, not appended to #12512

Closed

Description

@objectstack-fleet

Source

packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx, opened from the Studio home's "New package (writable base)" on 2026-10-15. Field order: Package ID (required, placeholder com.acme.crm, hint "Filled in from the display name until you edit it", key engine.packages.idRule.derived), then a Basics card with Display name, Object namespace, Version, Type (app) and Description, then Advanced. Typing "Studio Test" into Display name filled the id com.default.studio-test and the namespace studio_test, and Version came prefilled 0.1.0. Clicking into the namespace and typing st produced studio_testst; clicking into Version and typing 0.1.0 produced 0.1.00.1.0: the caret lands at the end of a value the user did not type, so the natural next keystrokes append.

Target

  1. Display name is the first field; Package ID, Object namespace and Version follow it inside the same card, each marked "derived from the name" until edited (a small chip or the existing hint next to each).
  2. Focusing a derived field selects its whole value, so typing replaces it; editing clears the derived mark for that field only.
  3. The namespace derivation is short: the name's initials when it has two or more words (Studio Test → st), the first word otherwise, lower-case; the hint already says it prefixes every object name.
  4. Create package stays disabled until id, name, namespace and version validate; each invalid field says why inline.

File surface

packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx, packages/app-shell/src/views/metadata-admin/i18n.ts (hints in every locale), tests beside.

Acceptance

  1. Tests: typing a display name fills id, namespace and version; focusing a derived field and typing replaces the value; a two-word name derives a two-letter namespace.
  2. Screenshot of the dialog before/after at 1280 px, light and dark.
  3. pnpm --filter @object-ui/app-shell test green.

Constraints

  • The id rule (reverse-domain) and the server-side validation do not change.
  • Existing i18n keys stay; new ones in every locale.

Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-15 Studio test pass: objectui main d4bc81eab, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, viewport 1482 × 1119, dark theme with a light-theme check.

Evidence

Screenshots from the test pass, kept on the temporary branch claude/studio-test-pass-evidence-20261015 of objectstack-ai/objectui (commit 72115588c, never merged: the maintainer ruled that screenshots do not enter main, and asked for a temporary branch referenced from the card). The branch's docs/design/studio-redesign/findings/2026-10-15/README.md maps every file to its card. Pane captures, 800 × 604 of a 1482 × 1119 viewport, dark unless named light.

new package form derived id
docs/design/studio-redesign/findings/2026-10-15/12512-new-package-form-derived-id.jpg — new package form derived id.

new package form top
docs/design/studio-redesign/findings/2026-10-15/12512-new-package-form-top.jpg — new package form top.

Parent: #12446

Body refreshed 2026-10-15T15:19Z by the epic PM session: an Evidence section with the test-pass screenshots added.

Activity

  1. added
    enhancementNew feature or request
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    on Oct 15, 2026
  2. added and removed
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    on Oct 15, 2026
  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (domain:ui lane queue)
    Session: session_01SJn1GEkrbbBQkhGC26wEDw
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12512-new-package-form
    Worktree: objectui-issue-12512 (in the cloud session's own container)
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx, packages/app-shell/src/views/metadata-admin/i18n.ts (the new hints in every locale), new tests beside them, the screenshots under docs/design/studio-redesign/after/ named 12512-*.png, one .changeset/12512-*.md (stop on breach; explain in the report)
    Container & model: M, mode:cloud, model: opus (cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 5 on 「上限提到 5,提速」, this session's chat, 2026-10-15)
    Clause-②: no
    Responsibility: n/a — form UX, the package create request is unchanged
    Thread-read: none
    Serial constraints cleared: no open PR touches PackageFormDialog.tsx. metadata-admin/i18n.ts is shared and ordinary concurrency.

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12512,
    "status": "done",
    "branch": "claude/issue-12512-new-package-form",
    "pr": null,
    "session": "session_01CBdmXKZZFem3WB6fiqnYLw — this run's harness-stamped id (commit trailer Claude-Session line); dispatched by PM seat domain:ui#4, session_01SJn1GEkrbbBQkhGC26wEDw",
    "premise_still_valid": true,
    "summary": "Targets 1, 2 and 4 are implemented in PackageFormDialog.tsx, with 3 new i18n keys in en-US and zh-CN. In create mode the dialog shows one Basics card: Display name, then Package ID (the shared PackageIdInput), Object namespace, Version, Type and Description. Every row except the id is still the spec form's own field, picked by name from getPackageForm. The id, namespace and version each carry a hint line while the value is still the dialog's. Focusing such a field selects its whole value, so typing replaces it. Editing a field clears only that field's mark. The touched flags moved from refs into state beside the draft. Create was already gated on all four validators; the missing piece was an inline reason for an empty id or namespace, and that is added. Target 3 (initials namespace) is not implemented, per the ruling. It is reported as an open question with the four-axis analysis. PM assumptions 1, 2 and 4 held as stated. Assumption 3 was measured: deriveNamespaceFromPackageId('com.default.studio-test') returns 'studio_test'. ManifestSchema and NAMESPACE_RE both accept the two-letter 'st' (and refuse 's'). The spec states a namespace 'Must be unique within a running instance', and the registry keeps namespace owners. Before the edit, the newest Claim comment (6196331069) named this branch. The card's assignee was not written.",
    "tests": "HEAD 910a057. The union run went through os-verify-lock, slot issue-12512: pnpm exec vitest run --maxWorkers=2 on every PackageFormDialog*, CreatePackageDialog*, EditPackageDialog*, PackageIdInput* and BuilderLanding* test, the new PackageFormDialog.derivedFields-12512.test.tsx and packages/i18n/src/tests/machineLocaleCensus-9909.test.ts. Result: 'Test Files 15 passed (15) / Tests 143 passed (143)', VERDICT command-exit 0. The diff adds no Intl.* call (grep count 0). The new file has 12 tests covering: the Basics card order (name, id, namespace, version); typing 'Studio Test' fills id com.studio-test, namespace studio_test and version 0.1.0, with all three marks shown; focusing any derived field selects 0..len; a user-event click then typing replaces the namespace ('st'), the version ('1.0.0') or the id ('com.acme.crm', namespace follows to 'crm'), and clears only that mark; an edited field keeps its caret on refocus; Create is enabled only after the name fills the rest; an emptied namespace and a CJK-only name both disable Create and say why; an invalid version disables Create with an inline issue; no reason shows before typing. Ablations on 910a057 used objectstack scripts/ablation-replace.mjs (anchor x1 -> x0, each run ended 'ok restored: blob == HEAD (c2c6365972ee)' with git diff HEAD empty and the ABLATED marker count 0): select-on-focus replaced with a no-op → 4 failed | 8 passed; display name moved after the version → 3 failed | 9 passed; the version edit not marking the version touched → 1 failed | 11 passed; namespace reason forced false → 1 failed | 11 passed; id reason forced false → 1 failed | 11 passed. The first namespace-reason ablation and a mouseup ablation were refused by the tool as no-ops (the replacement text already occurred in the file) and never ran, so both were re-run with marker replacements. The mouseup-guard ablation stayed green in jsdom (12 passed). Ablated in real Chromium, the selection still survived the click (0..11, typed 'st'), so the guard was removed in 910a057 instead of being pinned. Browser measurement against HotCRM 63db83e (objectstack dev --fresh -p 4071, console vite :5193, Playwright on /opt/pw-browsers/chromium, 1280 px): before (main's PackageFormDialog.tsx placed on disk temporarily, restore checked by blob hash), clicking the namespace gave selection 11..11 and typing gave 'studio_testst' in both themes; after, the selection was 0..11 and typing gave 'st' in both themes.",
    "gates": "Local, on HEAD 910a057, each exit code captured before any pipe: dependency closure pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build VERDICT command-exit 0; pnpm --filter @object-ui/app-shell type-check (echoed 'tsc --noEmit && tsc -p tsconfig.test.json') exit 0; pnpm --filter @object-ui/app-shell build exit 0 ('dist completeness: 1 package(s) complete (1104 emitted files verified)'); eslint --no-inline-config on the 3 touched source files exit 0, '3 problems (0 errors, 3 warnings)' against 2 on main for PackageFormDialog.tsx (the +1 is react-hooks set-state-in-effect on the org-slug effect; lint.yml deliberately sets no --max-warnings); pnpm check:i18n-keys 0; pnpm check:i18n-drift 0 ('3 key(s) added, 0 removed', 'No en value changed'); pnpm check:i18n-designer-parity 0 ('Every en row has a zh row'); pnpm check:i18n-dead-keys 0 (no nsRule, versionRule or idRule key among the candidates); pnpm check:new-line-citations 0 ('0 new citation(s)'); pnpm check:test-path-roots 0; node scripts/check-changeset-presence.mjs 0 ('3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'). objectui has no scripts/check-nul-bytes.mjs, so I ran the manual control-byte scan on the new files: 0 hits. CI check-runs: NOT MEASURED, reason: no PR is open (degraded channel, the PM opens it). Every CI family is owed to the PR's check-runs.",
    "mcp_calls": "2 GitHub MCP calls, both reads: mcp__github__issue_read (get, objectui#12512) and mcp__github__issue_read (get_comments, objectui#12512). Also 2 claude-code-remote add_repo calls with access 'read' for objectstack and hotcrm: both public, so nothing was attached and they were anonymous clones. No MCP write tool was called.",
    "api_writes": "0 REST writes. git push only (not a REST write), 5 pushes to claude/issue-12512-new-package-form: the empty branch at b512428, then dd15dc6, e660981, 7646190 and 910a057. Remote head verified at 910a057. No PR, comment, label or assignee write.",
    "files_changed": [
    "packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx",
    "packages/app-shell/src/views/metadata-admin/i18n.ts (+3 keys in en-US, +3 in zh-CN)",
    "packages/app-shell/src/views/metadata-admin/PackageFormDialog.derivedFields-12512.test.tsx (new)",
    ".changeset/12512-new-package-form-derived-fields.md (new, '@object-ui/app-shell': patch)",
    "docs/design/studio-redesign/after/12512-new-package-before-light.png",
    "docs/design/studio-redesign/after/12512-new-package-before-dark.png",
    "docs/design/studio-redesign/after/12512-new-package-after-light.png",
    "docs/design/studio-redesign/after/12512-new-package-after-dark.png"
    ],
    "line_budget": "n/a",
    "deviations": [
    "Contract vs dispatch on writes: os-dev.md says the dev opens the draft PR and posts the os-dev-report comment through the scripts/pm write tools (fleet-write relay). The dispatch says that relay is refused (403) from this container and routes both to the PM. I followed the dispatch and did not probe the relay, so the PR and the card comment are owed by the PM.",
    "Shared checkout: /home/user/objectui had a local branch claude/issue-12512-new-package-form checked out, with no commits of its own (at b512428). To delete it and create the worktree, I detached the shared checkout's HEAD. It is left detached at b512428, with nothing edited there.",
    "The first targeted run of the 13 existing test files ran without os-verify-lock. Every later test, type-check and build ran under the lock.",
    "Version mark wording: the card says each field is marked 'derived from the name', but the version is a fixed default, not derived from the name. Its hint reads 'A starting version, kept until you edit it.' instead.",
    "Marks are hint lines under each row, not chips (the card allowed 'a small chip or the existing hint'). The id reuses its existing engine.packages.idRule.derived line, now shown only while the id is still derived.",
    "Layout mechanism: create mode mounts SchemaForm five times (name; namespace; version; type+description; Advanced). Each is a one-section slice of getPackageForm picked by field name, inside a dialog-owned Basics card, with Tailwind arbitrary-variant classes dropping the inner section frame. Reason: PackageIdInput must sit between name and namespace, and SchemaForm has no slot or registry for a host widget. WIDGETS in widgets.tsx is a static object, and editing it or SchemaForm would breach the claimed file surface. package-schema.ts is untouched; its curated order is no longer what create mode shows. PackageIdInput.tsx is untouched: select-on-focus comes from a wrapper's bubbling onFocus, so no surface breach was needed.",
    "A mouseup guard was written, then removed in 910a057 after measurement. In real Chromium the focus handler's selection survives the click with and without it, and jsdom cannot see it either way. WebKit and Firefox were not measured.",
    "Lint warnings on PackageFormDialog.tsx went from 2 to 3: react-hooks set-state-in-effect on the org-slug effect, now that it updates the combined draft-and-touched state. It is the same pattern as main's open effect. No warnings ratchet exists.",
    "Commit trailers use AGENTS.md's model-free pair (Co-authored-by: Claude, plus Claude-Session) rather than the harness reminder's model-named Co-Authored-By. AGENTS.md takes precedence for these lines.",
    "Backend: HotCRM 63db83e via objectstack dev --fresh -p 4071. No writable base was created, because the screenshots stop before Create and nothing was submitted. The before and after runs both type 'Studio Test'. The after shots were retaken after 910a057 and are byte-identical to the ones committed in 7646190.",
    "Target 3 was not implemented and its acceptance test ('a two-word name derives a two-letter namespace') was not added, per the ruling. See open_questions."
    ],
    "open_questions": [
    {
    "question": "Target 3: should a new package's default namespace be the display name's initials ('Studio Test' → 'st') rather than the spec's derivation from the id's last segment ('com.default.studio-test' → 'studio_test')? Measured: the installed @objectstack/spec 18.0.0-next.1 deriveNamespaceFromPackageId returns 'studio_test'; ManifestSchema.shape.namespace and NAMESPACE_RE both accept 'st' and refuse 's'; manifest.zod.ts says a namespace 'Must be unique within a running instance', and metadata-protocol keeps namespace owners (getNamespaceOwners, RESOURCE_CONFLICT 409).",
    "options": [
    "A — Keep the spec derivation (last id segment). No change. With this PR the author replaces a derived namespace by clicking into it and typing.",
    "B — Open a spec card in objectstack to change deriveNamespaceFromPackageId (or add a name-based derivation) to initials, and let objectui follow.",
    "C — Derive initials in objectui only. Excluded by the ruling: objectui does not fork a second derivation."
    ],
    "recommendation": "A. Real business need: the card's measured defect was the append ('studio_testst'), which this PR addresses. No measured producer writes initials: the authored namespaces measured are hotcrm 'crm', objectstack examples 'showcase' and 'todo', all whole words. So the demand for initials comes from the card's prose, not from usage. Long-term soundness: one derivation in the spec feeds both the Studio dialog and the platform's default for manifests that omit a namespace. Diverging them would make the same id yield two different namespaces, and changing the spec moves the default for every existing namespace-less package. AI error-resistance: the namespace prefixes every object name and must be unique per instance. Two-letter initials collide sooner ('Studio Test' and 'Sales Tracker' are both 'st'), which shifts the failure to an install-time 409 or a rename later, and initials say less to an AI reading object names like st_task. Startup focus: B adds a spec change and a migration story for no measured pull. If the maintainer still wants shorter defaults, that is B as a spec card, not this card."
    }
    ],
    "out_of_scope_findings": [
    "Observation, read from the code, not exercised: a whitespace-only display name keeps Create disabled without an inline reason (the dialog trims it to empty; the manifest's name schema accepts the untrimmed string, so no spec-form issue). reach: none measured. Noted in the PR's Acceptance notes, not filed. carrier: none."
    ],
    "pr_title_draft": "feat(app-shell): new package form opens on the display name; derived id, namespace and version are replaced on edit",
    "pr_body_draft": "(elided by the seat: opened verbatim as the body of PR #12551, read back identical)"
    }

    Relayed verbatim by seat domain:ui#4 (session session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message, the degraded write channel. The seat opened the draft PR #12551 from pr_title_draft and pr_body_draft, with assignee os-sales. "pr": null above is the session's own value at report time.


    Generated by Claude Code

  5. added a commit that references this issue on Oct 28, 2026
    eea0993
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portaldomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions