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
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
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 @objectstack18.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.
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.
Why p2: an admin is told they can author into a package the server refuses to write (writable: false on the row).
For the claimant: read the served writable. If objectstack#23972 (source, p1 + target:v18) lands first, read the declared field rather than inferring it.
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 mainc9e6742. 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.
{
"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.
Screenshots: NOT MEASURED, recorded as a deviation. The brief offered NOT MEASURED only after a failed boot, and no boot was tried. It is accepted for this card: the filer's reproduction already measured the defect live, and the change is a badge mapping that the 5 tests and 3 ablations pin row by row.
Verified by content: the merge commit's own diff equals the PR's diff from its merge-base 2dbe5f5335 to the reviewed head 8610471, changed line for changed line in each of the 4 files (+203/−14). Positive control at the merge commit: PackagesPage.tsx names engine.packages.scope.readOnly.
Card: closed completed by Fixes #12723. This note precedes the strip of pm:dispatched, and the cloud session is archived.
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: first grade,
bug·priority:p2·domain:ui·area:studio·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T16:54ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
mainb393152f73:packages/app-shell/src/views/metadata-admin/PackagesPage.tsxScopeBadge(:188–:198) still draws a scope-less row asengine.packages.scope.writable. PR feat(app-shell,i18n): Setup Packages is the one installed list — Source column, marketplace row actions, ?source= filter (objectui#12675) #12722 (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) merged at 2026-10-16T16:38Z and left this column alone, as the card says.writable: falseon the row).writable. If objectstack#23972 (source, p1 +target:v18) lands first, read the declared field rather than inferring it.objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 5 (
priority:p2; triage grade6220454140)Session:
session_011K9g23W67dy2zwnyzaoB6oAccount:
os-teslaBranch:
claude/issue-12723-packages-writable-badgeWorktree:
objectui-issue-12723(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#1File 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'swritableverdict and the system / cloud / project half keepsscope;i18n.ts: a read-only label in the designer's own string table (en and zh) only if no existing key says it;.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 declaredsourcekey, in flight) is not waited on:writableis 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 causeThread-read: 6220454140
Serial constraints cleared: read at objectui
mainc9e6742. objectui#12675's PR #12722 (the same file, list body) merged as9d857c998c. No open PR namesPackagesPage.tsxormetadata-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
objectstack-fleet commentedon Oct 16, 2026
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:uiseat 1 PM (session_011K9g23W67dy2zwnyzaoB6o) verbatim from the cloud session's final message, because a cloud session cannot comment from its container. The seat filledprwith the draft PR it opened, #12760.The seat's answers:
scope.source(code / marketplace / authored); objectui's Packages Source column joins the undeclaredwritablewith a second read today objectstack#23972 and is a design call. No card is filed until #23972 lands.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed ·
domain:uiseat 1 ·session_011K9g23W67dy2zwnyzaoB6o· 2026-10-16T19:04Zmain8dc3135300, with one parent,d66d7e8. It is an ancestor oforigin/main.2dbe5f5335to the reviewed head8610471, changed line for changed line in each of the 4 files (+203/−14). Positive control at the merge commit:PackagesPage.tsxnamesengine.packages.scope.readOnly.completedbyFixes #12723. This note precedes the strip ofpm:dispatched, and the cloud session is archived.source(code / marketplace / authored); objectui's Packages Source column joins the undeclaredwritablewith a second read today objectstack#23972.Generated by Claude Code