Skip to content

Setup Packages: the Scope badge reads "Writable" for a marketplace install the server marks not writable — ScopeBadge draws every scope-less row as writable and ignores the served writable verdict #12723

Closed

Description

@objectstack-fleet

Filing gate ①: a class-a finding from the os-dev report on objectui#12675 (comment 6218334568, out_of_scope_findings[0]). Filed by the domain:ui seat 1 (session_011K9g23W67dy2zwnyzaoB6o). ⛔ Not a claim; for triage to grade and route.

reach: public door. In Setup → Packages, every admin who has installed a marketplace package sees it badged "Writable". Measured live on objectui main f8267df93 against a published @objectstack 18.0.0-next.2 showcase backend: com.example.demo-todo was installed through POST /api/v1/marketplace/install-local, the badge says Writable, and GET /api/v1/packages serves writable: false for that row.

Measured

  • The badge. packages/app-shell/src/views/metadata-admin/PackagesPage.tsx, ScopeBadge (around :172), draws any row with no scope as engine.packages.scope.writable. A scope-less row is assumed to be a database base package. The served row's own writable verdict (objectstack#14375, pinned by objectstack's packages-serializable-response.test.ts) is never read.
  • Why it misleads. A marketplace install has no scope and is not writable. The badge tells an admin they can author into a package the server refuses to write.
  • Evidence: evidence/12675/before-packages.png and after-packages.png on branch claude/issue-12675-evidence.
  • Not changed by objectui#12675. PR feat(app-shell,i18n): Setup Packages is the one installed list — Source column, marketplace row actions, ?source= filter (objectui#12675) #12722 adds a Source column (Code / Marketplace / Authored) and leaves the Scope column alone, so after it lands the row reads Source "Marketplace" beside Scope "Writable".

The fork (for triage to scope)

  1. The badge reads the served writable verdict for the writable / read-only half, and keeps scope for system / cloud / project.
  2. Or the Scope column folds into the Source column once Source is a declared key on the served row. That declared key is the objectstack half the seat files from Setup Packages is the one list of what is installed: Source column, marketplace row actions, and the "Installed Apps" entry opens Packages filtered to Source = marketplace (ruling B, objectstack-ai/objectstack#23892) #12675's report.

Per the four axes, a badge that contradicts the server's verdict is the mask to remove. Whether two columns stay is a design call.

Dedupe: search_issues in objectui with "Packages Scope badge Writable marketplace install scopeless writable verdict ScopeBadge" returned 1 result, #7431 (closed; the studio writable fallback's premise). It does not cover the Packages badge.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p2 · domain:ui · area:studio · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T16:54Z

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.

  2. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 16, 2026
  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 5 (priority:p2; triage grade 6220454140)
    Session: session_011K9g23W67dy2zwnyzaoB6o
    Account: os-tesla
    Branch: claude/issue-12723-packages-writable-badge
    Worktree: objectui-issue-12723 (in the cloud session's own container)
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: Setup → Packages' Scope badge, in packages/app-shell/src/views/metadata-admin/:

    • PackagesPage.tsx: ScopeBadge (:188) and its two call sites (:716, :1375), so the writable / read-only half reads the served row's writable verdict and the system / cloud / project half keeps scope;
    • i18n.ts: a read-only label in the designer's own string table (en and zh) only if no existing key says it;
    • tests beside them;
    • one .changeset/12723-*.md.

    Outside the surface: the Source column and packageSource.ts (objectui#12675, landed), the page header, the marketplace row actions, and any objectstack change. objectstack-ai/objectstack#23972 (the declared source key, in flight) is not waited on: writable is already served and pinned (objectstack#14375).

    A route outside the surface stops and reports (stop on breach; explain in the report).
    Container & model: S, mode:cloud, model: opus (default tier; no path-derived mandate)
    Clause-②: no
    Clause-② reason: the badge stops contradicting a verdict the server already serves. No published key, export or accept set changes, and a designer string-table key is not a published pack key.
    Responsibility: n/a — ScopeBadge's scope-less-means-writable rule predates the marketplace install path; no landed PR is named as its cause
    Thread-read: 6220454140
    Serial constraints cleared: read at objectui main c9e6742. objectui#12675's PR #12722 (the same file, list body) merged as 9d857c998c. No open PR names PackagesPage.tsx or metadata-admin/i18n.ts (every open PR's file list was read). objectui#12733 (pm:blocked) waits on objectstack-ai/objectstack#23933 and is not in flight.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12723,
      "status": "done",
      "branch": "claude/issue-12723-packages-writable-badge",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12760",
      "session": "session_01FEQ7emhap8DeqSxHgGsGPe",
      "premise_still_valid": true,
      "summary": "H1 confirmed at c9e6742: ScopeBadge took only `scope` and drew every scope-less row as engine.packages.scope.writable. ScopeBadge now takes the whole row. Writable vs. read-only comes from the served `writable` (objectstack#14375; stamped by `isWritablePackage` and pinned by objectstack's packages-serializable-response.test.ts 'stamps the #14375 `writable` verdict AFTER the projection, on both doors', read at objectstack 61a6809). `scope` only names which kind of read-only package a row is. Combined table: writable true reads Writable whatever the scope. writable false reads System, Cloud or 'Read-only · code' by scope, and a scope-less row reads the new designer key engine.packages.scope.readOnly (en 'Read-only', zh '只读'). The serializer allows four live combinations: system/cloud only false; project true (a DB base) or false (a booted code package); scope-less true (an authored base) or false (a marketplace install or a scope-less booted code package). H4 corrected: the call site at the old line 716 is the package detail sheet, not a duplicate picker. Both call sites now pass the row, so the list and the sheet cannot disagree. H5 is pinned on #12675's marketplace row shape: Source 'marketplace' beside Scope 'Read-only'. The badge also drops one `as any`.",
      "tests": "New file packages/app-shell/src/views/metadata-admin/PackagesPage.scopeWritable-12723.test.tsx, 5 tests: scope-less marketplace install with writable false reads Read-only, never Writable, with Source marketplace; scope-less authored base with writable true reads Writable; scope-less code, system, cloud and project code (false) plus project base (true), with system/cloud read after turning on the show-kernel switch; absent `writable` is refused at load and no badge or row is drawn; the detail sheet matches the row. Final head 8610471: `pnpm exec vitest run packages/app-shell/src/views/metadata-admin/PackagesPage` gave 'Test Files 8 passed (8) / Tests 65 passed (65)', exit 0. The full metadata-admin family ran at d85afdb: 'Test Files 1 failed | 513 passed (514), Tests 1 failed | 5785 passed | 1 skipped'. The one failure was the new test's system/cloud row, hidden behind the show-kernel switch; fixed in c3a8b43, and that file then passed 5/5. A grep over packages/, apps/ and scripts/__tests__ finds ScopeBadge and engine.packages.scope only in PackagesPage.tsx and i18n.ts; the other families are covered by the PackagesPage run. Ablation, by script with a trap and committed fix, at c3a8b43; the test imports source, so no dist leg was needed. Each mutation of the line `if (writable) {` was proven on disk (injected=1, anchor=0). A: `writable || !pkg.manifest.scope` (the old defect) made 3 tests fail (marketplace, scope table, detail sheet). B: `writable && scope !== 'project'` made 1 fail (scope table). C: `writable && !!pkg.manifest.scope` made 1 fail (authored base). After each, the restored blob 6d9bf630557b4b4cea936c940f1a98eacd88a142 equalled the HEAD blob and `git diff HEAD` was empty. The absent-writable test pins the existing refusal from #12675 and is not ablated.",
      "gates": "Exit codes were captured before any pipe. The dependency build `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build` (exit 0) and `pnpm --filter @object-ui/app-shell type-check` (exit 0; runs tsc --noEmit plus tsc -p tsconfig.test.json) ran at c3a8b43; the merge did not touch app-shell's files. At head 8610471: check:i18n-keys, check:i18n-drift, check:i18n-dead-keys, check:i18n-designer-parity, check:changeset-claims, changeset:check, check:test-path-roots, check:spec-symbols and check:new-line-citations ('0 new citation(s)') all exit 0. `node scripts/check-changeset-presence.mjs` exit 0 ('declares 1 changeset(s)'). eslint on the 3 changed source files exit 0 with 16 warnings, all on existing lines; the base file has 17, and this diff removes the `variant as any`. `scripts/check-nul-bytes.mjs` does not exist in objectui (MODULE_NOT_FOUND, not a gate failure); a hand scan for control bytes in the changed files found 0. CI check-runs: NOT MEASURED, no PR yet.",
      "mcp_calls": "2 — mcp__github__issue_read (get, get_comments); no write tool",
      "api_writes": "0 — the only GitHub writes are 2 git pushes of the branch (the empty claim marker at c9e6742, then 8610471)",
      "open_questions": [
        { "question": "What does the badge draw for a row with no boolean `writable`?", "options": ["A: nothing new. The list already refuses that row at load (PACKAGE_SOURCE_UNKNOWN), and the badge itself, which cannot be reached, draws an unknown dash and never Writable", "B: also throw or refuse inside the badge"], "recommendation": "A. One loud refusal at the boundary #12675 already owns; a second refusal path in an unreachable branch adds nothing. The test pins that no badge is drawn." },
        { "question": "A project-scoped row the server marks writable used to read 'Read-only · code'. It now reads Writable. Keep that?", "options": ["A: the verdict wins (as built)", "B: keep 'Read-only · code' for every project row"], "recommendation": "A. `isWritablePackage` returns true for a project DB base, so B would keep a contradiction of the same kind this card removes." },
        { "question": "Should the Scope column later fold into Source (the card's option 2) once objectstack#23972 declares `source`?", "options": ["A: leave it for a later card once #23972 lands", "B: fold now"], "recommendation": "A. The triage grade says read the served `writable` now. Whether to keep two columns is a design decision for the maintainer." }
      ],
      "out_of_scope_findings": [
        "NOT MEASURED: screenshots. No backend was booted in this session, so there are no before/after PNGs; the behaviour is pinned by the 5 tests and the ablation above.",
        "Worktree ../objectui-issue-12723 is left in place (node_modules included) until the seat opens the PR, as the role file's cleanup step requires."
      ]
    }

    Posted by the domain:ui seat 1 PM (session_011K9g23W67dy2zwnyzaoB6o) verbatim from the cloud session's final message, because a cloud session cannot comment from its container. The seat filled pr with the draft PR it opened, #12760.

    The seat's answers:


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 1 · session_011K9g23W67dy2zwnyzaoB6o · 2026-10-16T19:04Z


    Generated by Claude Code

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 portalbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions