Skip to content

Studio: customize an installed package's page, so a customer can change a home or landing page without a code release #30063

Closed

Description

@objectstack-fleet

epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

The maintainer: 「全部立,继续测试验证」 (2026-11-04), on the seat's evaluation of which metadata types should support override.

Measured by the seat through the metadata API and in a browser on main 63b86b385d, on examples/app-crm and examples/app-showcase, each overriding through its paired customization package. No domain:* label is set: triage routes this card.

Business reason

A customer installs an app and wants its welcome or landing page to say their own thing, or to show different blocks. Today Studio shows the page as 只读 and offers no way forward. The platform already accepts the change: only the door is missing.

Measured

  • The override of crm_welcome in com.example.crm.custom: save 200, publish published, listed by GET /api/v1/packages/com.example.crm/customizations.
  • In the running app, /apps/com.example.crm/page/crm_welcome showed the changed text and description.
  • Removal returned the shipped page. Also measured on showcase's showcase_capability_map.
  • The Studio header on that page reads 只读, 设计, 运行, with no 定制. A dashboard in the same app offers 定制.

Where

ui/packages/app-shell/src/views/studio-design/StudioCustomizeControls.tsx:73: STUDIO_CUSTOMIZE_TYPES is view, dashboard and report. page is overridable by OVERRIDE_TYPE_CLASSIFICATION.

Acceptance

  1. A read-only base's page offers 定制. The first save copies it whole into the pair with the declaration, and 恢复原版 removes it, as a dashboard does.
  2. The page designer edits the pair's copy, and a designer edit shows in the running app after publish.
  3. Marks, the 仅看定制 count and the All metadata tile cover pages.
  4. A browser check on the example CRM, with screenshots on the PR.
  5. No server or spec change is expected. If one is needed, report it here.

Coordinate with

The override-label defect filed beside this card: a renamed page's label is hidden by the base's translation today, so a page's rename depends on that fix. #30059 opens app the same way; the two may share the STUDIO_CUSTOMIZE_TYPES line.

Re-check

git grep -n "STUDIO_CUSTOMIZE_TYPES: readonly" origin/main -- ui/packages/app-shell/src/views/studio-design/StudioCustomizeControls.tsx

Activity

  1. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 kept · domain:ui · 2026-11-04T13:10Z

    ⛔ Not a claim.

    • Read fresh. Studio customization of an installed package's page (a home or landing page) without a code release. Page is already override-served. p2 kept.

    Generated by Claude Code

  2. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC

    Claim: PM loop round 1
    Session: session_01WGufzhKo5SDER9fGbQQqSa
    Account: os-support-ai (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/project-thread-6e8nin
    Worktree: objectstack-issue-30063
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/views/studio-design/StudioCustomizeControls.tsx (types list + page door only), the page designer and the customize/restore/marks paths in ui/packages/app-shell/src/views/studio-design/**, i18n strings in ui/packages/i18n as needed, their tests, one root .changeset/ file; ⛔ no server or spec change (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (p2, judgment card), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6631494898
    Premise: holds
    Serial constraints cleared: #30064 (domain:ui#2, branch claude/issue-30064-studio-dataset-customize) also edits StudioCustomizeControls.tsx: whichever lands second merges origin/main before leaving draft. #30059 landed 6cc0b818 in the same file. No other open PR touches StudioCustomizeControls.tsx (read this act).

  3. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb

    Serial note · 2026-11-05T01:20Z. #30062 item 4 (claim 6646593490, branch claude/project-thread-yenpdr) changes typeStateWord in ui/packages/app-shell/src/views/metadata-admin/PageShell.tsx: on a read-only package, the All metadata tile's "可定制" will read the customize door's own type table (isStudioCustomizeType + AREA_FORM_EDITOR_TYPES + flow) instead of allowEnvironmentOverlay. It does not touch StudioCustomizeControls.tsx, so when this card adds its door the tile follows. Whichever PR lands second updates the #29705 test's tile expectation for page / dataset from 只读 to 可定制. No file intersection; the merge queue arbitrates.

  4. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-support-ai · session_01WGufzhKo5SDER9fGbQQqSa
    os-dev-report
    Stamp: 2026-11-05T02:51Z

    {
      "issue": 30063,
      "status": "done",
      "branch": "claude/project-thread-6e8nin",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30233",
      "session": "session_01WGufzhKo5SDER9fGbQQqSa",
      "premise_still_valid": true,
      "summary": "Studio's Customize door now covers page: STUDIO_CUSTOMIZE_TYPES gains 'page' (isOverridableMetadataType already true). Everything else was type-generic in the Interfaces pillar and is now reached for pages: Customize in the header, the first designer edit copies the whole page into BASE.custom with overrides {package, basedOn} via the ensure door, later edits save into the copy, Restore package original deletes it, and the rail mark, the only-customizations count and the All metadata page tile read the server's ledger. No spec, server or metadata-type change. Head a73ecd64 (origin/main dfafae81 merged, clean).",
      "tests": "InterfacesPillar.customize-28562.test.tsx gains a top-level describe for objectstack#30063 (5 tests: first save into the pair with the declaration and ifNoneMatch, then ifMatch v1; served override editable; Restore DELETE draft then row at the pair; rail mark + only-customizations 1; CONTROL writable package saves in place with no overrides). StudioCustomizeControls test moves page to the customized list; metadataAreaHost.customizations-28568 test pins the page tile count. Three touched files: 73/73 passed. Whole studio-design dir: 324 files / 2397 tests passed; after the main merge (full build 119/119 first): 325 files / 2415 tests passed (VERDICT command-exit 0). app-shell type-check exit 0 before and after the merge. Ablation at eee39383 via ablation-replace.mjs ('report', 'page', 'app' -> page removed; ui vitest resolves @object-ui to src, no build leg): 3 red (first-save, served-override, Restore pins), 2 green as expected (rail mark reads the ledger; writable CONTROL); restored blob 1452690e == HEAD, git diff HEAD empty. Browser on a dev boot of examples/app-crm (head eee39383, zh-CN, admin): Customize -> edit text block -> ensure 201 + PUT draft into com.example.crm.custom 200 -> publish-drafts 200 -> /apps/com.example.crm/page/crm_welcome shows the edit; rail mark, 仅看定制(1), All metadata 已定制 1; Restore -> two DELETEs 200, shipped page back in Studio and runtime, ledger empty, base body identical before/after. 9 shots in /mnt/project-files/ui-orders/30063-shots/.",
      "gates": "dispatch-gates --commands at a73ecd64: 35 families (same 35 as before the merge), every one exit 0. dispatch-gates --ran: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. node scripts/check-sdui-manifest.mjs: exit 0. npx eslint on the 4 touched files: 0 errors, 15 pre-existing react-refresh warnings. Record: /mnt/project-files/ui-orders/30063-gates-ran.list. CI check-runs: not read at report time; not waited on.",
      "line_budget": "244 changed lines (+233 / -11, 5 files) vs the 1,500 split line and the 3,000 ceiling: under both; Acceptance 1-4 ship in one PR.",
      "files_changed": [
        ".changeset/30063-studio-customize-page.md",
        "ui/packages/app-shell/src/views/studio-design/StudioCustomizeControls.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioCustomizeControls.customize-28562.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/InterfacesPillar.customize-28562.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/metadataAreaHost.customizations-28568.test.tsx"
      ],
      "deviations": [
        "File surface: metadataAreaHost.customizations-28568.test.tsx sits in views/metadata-admin, outside the claimed studio-design/** surface; it is the existing harness for the All metadata tile (Acceptance 3), so the page-tile pin went there as a new it().",
        "Commit trailer: the harness asked for a model-named Co-Authored-By; the repo's AGENTS.md trailer rule (model-free pair) was used instead.",
        "Session prompt's assign-and-request-review step after pr_create skipped per os-dev.md rule 2.",
        "The browser check ran on eee39383, before the origin/main merge; the merge changed no line of this PR's diff and the suites, type-check and gates were rerun on a73ecd64.",
        "My first full build was started without the docs filter; it was stopped through the harness task stop (a kill by PID was refused by the classifier) and rerun with --filter=!@objectstack/docs. Nothing else was running under it.",
        "The order lists four final-report fields; the os-dev.md standard fields are kept beside them, since os-dev.md says its fields always apply."
      ],
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP GitHub calls",
      "api_writes": "2 — relay pr_create (PR #30233, draft, no assignees, no labels; body read back identical, 11718 bytes) and this os-dev-report comment via post-stamped.mjs; plus two git pushes of the branch",
      "open_questions": [],
      "security_findings": "withheld",
      "out_of_scope_findings": [
        "class: a · reach: Studio on examples/app-crm (browser): after 恢复原版 on a customized page the rail keeps the customized mark and 仅看定制(1)until a reload (shot 08; a reload shows 0 marks and no filter) · evidence: StudioDesignSurface reads marks with usePackageCustomizations(packageId, publishNonce) and restoreOriginal never bumps publishNonce, a path shared by dashboard, view, list view and app · dedupe words: restore original stale customization mark, only customizations count after restore, usePackageCustomizations refresh",
        "class: a · reach: All metadata directory on examples/app-crm (shot 06): the page tile's state word reads 只读 while Studio customizes pages (the app tile likewise) · carrier: #30062 item 4, PR #30226 (typeStateWord reads isStudioCustomizeType); whichever lands second flips the #29705 test's page expectation to 可定制"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC

    Landed · PR #30233 · 2026-11-05T03:23Z

    probe reading
    merge MERGED 797f9257c9 at 2026-11-05T03:23:04Z, via the merge queue (seat ACCEPT 6648568672)
    card closed completed by Fixes #30063
    content on origin/main STUDIO_CUSTOMIZE_TYPES includes 'page' in ui/packages/app-shell/src/views/studio-design/StudioCustomizeControls.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions