Repository navigation
Setup → Packages: the Source column and the ?source= filter read the served row.source, and the two-read join (writable × the marketplace installed list) retires #12790
Description
Filing gate ④, a cross-repo child: the objectui half of objectstack-ai/objectstack#23972 (domain:spec, p1, target:v18). The objectstack half is PR objectstack-ai/objectstack#24010, merge commit 66d2757024.
- Filed by: objectstack
domain:specseat 2 (seat post [PM seat] domain:spec · seat 2 — 🟢 zhuangjianguo · 由 zhuangjianguo 账户的项目负责 objectstack#18549) ·os-bill· sessionsession_01LFqdRsuM5nkqoJdHd5ztVk. ⛔ Not a claim. - Who acts: the objectui seat that triage routes this card to. The grade and the route are triage's.
The ruling this serves
Ruling B on objectstack-ai/objectstack#23892 (6216295104, the maintainer's 「同意」 on batch #356), item 1, verbatim: "Setup's Packages (developer:packages) is the one list of what is installed: a Source column with the closed values code / marketplace / authored". objectui#12675 (PR objectui#12722, landed 9d857c99) delivered the column, reading two signals because the server declared no origin. The server now declares one.
What objectstack serves now
- Every
GET /api/v1/packagesandGET /api/v1/packages/:idrow carriessource:code,marketplaceorauthored, from a closed set. The door that registered the package states it, and no reader derives it. The declaration isInstalledPackageSourceSchemain@objectstack/spec/kernel, andInstalledPackageSchema.sourceis required. - The same rows carry
writable, now declared on the served row:InstalledPackageAtEitherStageSchemain@objectstack/spec/api-assembled, a required boolean on both stage branches. - The two keys are separate statements:
sourcesays where the package came from, andwritablesays what the server lets an org do to it. ⛔ Neither is derived from the other.
Measured (objectui main 8cc02b44)
packages/app-shell/src/views/metadata-admin/packageSource.ts:resolvePackageSource(:96) derives the origin.writable: truemeansauthored(:105). A non-writable row ismarketplacewhen its id is in the marketplace installed list, andcodeotherwise.- Two refusals follow from that join:
PACKAGE_SOURCE_UNKNOWN(:61, no booleanwritable) andPACKAGE_SOURCE_CONFLICT(:63, writable and listed at once). - The header (
:15) says "/api/v1/packagesserves no source key", which stops being true once feat(spec): the installed-package row declares its origin, a closed source (code / marketplace / authored), and the served writable verdict objectstack#24010 is in the runtime the console talks to.
packages/app-shell/src/views/metadata-admin/PackagesPage.tsximportsreadMarketplaceInstalls(:88). It makes the second read when the marketplace is on (:1131) and callsresolvePackageSource(p, ids)per row (:1146).
What done looks like
- The Source column and the
?source=filter readrow.source. The second read (readMarketplaceInstalls) and the join leave this page. PACKAGE_SOURCE_CONFLICTretires, because no join is left to disagree.- A row whose
sourceis absent or outside the closed set is refused at this boundary with a named code, never derived and never rendered as an empty cell. That is the strictnessPACKAGE_SOURCE_UNKNOWNhas today, applied to the declared key. - The row type comes from
@objectstack/spec(InstalledPackageAtEitherStage), not a local re-spelling.PACKAGE_SOURCESfollowsInstalledPackageSourceSchema's options or is pinned equal to them. - Pins:
- each of the three values renders and filters;
- an absent or unknown
sourceis refused by name; - CONTROL: a non-writable
authoredrow (an authored install with asystem/cloudscope) readsauthored, which the join used to callcodeormarketplace.
- ⛔ No fallback to the join when
sourceis missing. Declare equals enforce.
When this can start
objectui consumes @objectstack/spec from the registry (packages/app-shell/package.json: "@objectstack/spec": "^18.0.0-next.2"). So this card needs an installable @objectstack/spec prerelease that exports InstalledPackageSourceSchema, plus a runtime that serves source. objectstack main alone is not enough. To check: npm view @objectstack/spec@next version, then confirm that the installed @objectstack/spec/kernel exports InstalledPackageSourceSchema.
Dedupe: MCP search_issues in objectui, closed included. Packages Source column row.source writable marketplace join returned 0 hits. Setup Packages page source code marketplace authored filter installed list returned 1 hit, objectui#12675: the column itself, closed, whose join this card replaces.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: first grade,
enhancement·priority:p2·target:v18·domain:ui·area:studio·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T20:56ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
target:v18: it retires a two-read join that works today, now that objectstack PR #24010 (66d2757024) serves a declaredsource. Its absence breaks no user, so it is not p1.row.sourcewhen the backend serves it. The installed next.2 does not type it, so read it untyped through the existing client until a spec release that declares it is installed, and drop thewritable× installed-list join. objectui#12723 (p2, dispatched) is on the same page; check for overlap.objectstack-fleet commentedon Oct 17, 2026
Hold:
pm:queue→pm:on-hold(upstream not installable yet) ·domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-17T00:28Z⛔ Not a claim. This seat read the card at selection time and holds it by the cross-repo unlock rule: the criterion is that the consumer can install the upstream change, not that it has merged.
Measured just now:
npm view @objectstack/spec dist-tags:nextis18.0.0-next.2, published 2026-10-16T03:11Z.66d2757024), was committed 2026-10-16T20:09Z, after that prerelease. So no installable@objectstack/specexportsInstalledPackageSourceSchema, and no released runtime servesrow.source.The card's Done-when forbids a fallback to the join when
sourceis absent ("Declare equals enforce"). Against today's installable backend, every row would then be refused, so dispatching now would break the page for anyone on18.0.0-next.2. The triage note's "read it untyped" helps the typing, not the missing key.Restart-when:
npm view @objectstack/spec@next versionnames a prerelease newer than18.0.0-next.2, and that tarball'skernelentry exportsInstalledPackageSourceSchemaDispatch shape on wake (pre-written): one os-dev on
packages/app-shell/src/views/metadata-admin/packageSource.tsandPackagesPage.tsx, after the@objectstack/specbump carrying the schema is on objectuimain. Readrow.source; retirereadMarketplaceInstallson this page andPACKAGE_SOURCE_CONFLICT; refuse an absent or unknownsourceby name; use the spec's row type. Pins as the card lists them. objectui#12723, the same page, is closed, so there is no overlap.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 2 · hold released (
priority:p2,target:v18; the hold comment6227336980pre-wrote this dispatch's shape)Session:
session_0188TsgPV8kQXfuRuKmkj2ksAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12790-packages-served-sourceWorktree:
objectui-issue-12790Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/app-shell/src/views/metadata-admin/packageSource.ts(read the servedrow.source; retireresolvePackageSource'swritable× installed-list join andPACKAGE_SOURCE_CONFLICT; refuse an absent or unknownsourceby name;PACKAGE_SOURCESfollows or is pinned equal toInstalledPackageSourceSchema's options),packages/app-shell/src/views/metadata-admin/PackagesPage.tsx(drop this page'sreadMarketplaceInstallsread and the per-row join; use the spec's row type), the tests beside both,packages/i18n/src/locales/*.tsonly where a refusal message key changes, and one.changeset/12790-packages-served-source.md. ⛔readMarketplaceInstallsitself stays inpackages/app-shell/src/console/marketplace/marketplaceApi.ts, whichMarketplacePage.tsxreads. Stop on breach; explain in the report.Container & model:
M,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: no
Clause-② reason:
packageSource.tsandPackagesPage.tsxare reachable from no@object-ui/app-shellexport: the package'sexportsmap carries.and./styles.cssonly, and the entry re-exports neither module. The page reads a key the spec now declares; nothing published widens or narrows.Responsibility: n/a — not a defect card
Thread-read: 6227336980
Serial constraints cleared: read at objectui
main9b82ced.views/metadata-admin/packageSource.tsorPackagesPage.tsx. The open PRs are this seat's fix(console,i18n): delete the approval drawer's raw payload panel for every viewer (objectui#12490) #12934 (approvals inbox and locale packs, in the merge queue) and three bot PRs.pm:dispatchedclaims: objectui#12933 (seat 2) is inplugin-detail, and objectui#12924 (seat 3) and approvals face 4 of 5, dropped by the maintainer: delete the inbox side sheet's raw JSON panel, its studio.access gate and its pin (was: the raw-payload panel on the served page) #12490 (this seat) touchpackages/i18n/src/locales/*.tsin other regions. Whichever lands second mergesmain.Hold release (double check):
6227336980namesRestart-when:"npm view @objectstack/spec@next versionnames a prerelease newer than18.0.0-next.2, and that tarball'skernelentry exportsInstalledPackageSourceSchema". The packed@objectstack/spec@18.0.0-next.3tarball'sdist/kernel/index.*carriesInstalledPackageSourceSchema. objectuimain9b82cedresolves18.0.0-next.3. objectstack's release commit2c9bf9430ais 48 ahead and 0 behind PR feat(spec): the installed-package row declares its origin, a closed source (code / marketplace / authored), and the served writable verdict objectstack#24010's66d2757024(REST compare), so the servedsourceis in the release too.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{
"issue": 12790,
"status": "done",
"branch": "claude/issue-12790-packages-served-source",
"pr": "#12939",
"session": "session_0188TsgPV8kQXfuRuKmkj2ks",
"premise_still_valid": true,
"summary": "Setup Packages' Source column and ?source= filter now read each /api/v1/packages row's served
source. readServedSource refuses an absent or unknown value as PACKAGE_SOURCE_UNKNOWN, naming the package id. resolvePackageSource, the writable x installed-list join and PACKAGE_SOURCE_CONFLICT retire, and there is no fallback. The row'ssource/writabletypes come from InstalledPackageAtEitherStage (api-assembled), with PackageSource = InstalledPackageAtEitherStage['source']. PACKAGE_SOURCES is asatisfiestuple, test-pinned equal to InstalledPackageSourceSchema.options and to both stage branches'sourceoptions. It is not imported at runtime because spec 18.0.0-next.2, the app-shell floor, lacks the symbol.writablestays the Scope badge's own read and is still refused at load, now under its own name (PACKAGE_WRITABLE_UNKNOWN, key engine.packages.scope.unknown). DEVIATION from the card's literal bullet 'readMarketplaceInstalls leaves this page': on main 9b82ced that read also feeds the marketplace row actions (Details/Uninstall, moved here by objectui#12675) and the not-loaded install rows (objectui#11645). Neither can be derived from a packages row, and objectstack's 'Installed Apps' nav now opens this page. So the read stays for those two uses only and never names a registry row's Source; see open_questions[0]. Premise checks: Zone 2 item 1 holds (installed spec 18.0.0-next.3: kernel InstalledPackageSourceSchema options code/marketplace/authored; api-assembled InstalledPackageAtEitherStageSchema with required source + writable on both branches; no liveness ledger row for these keys). The 'MarketplacePage.tsx reads readMarketplaceInstalls' premise is false: only PackagesPage calls it. The Clause-2 'reachable from no export' premise is false at module-graph level but true at export-surface level. Worktree removed after push.","tests": "Final head 905168f: pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json) passed with 0 'error TS'. vitest (repo-root form, --maxWorkers=2) over PackagesPage*, packageSource*, EditPackageDialog* and console/marketplace/tests/: 'Test Files 47 passed (47) / Tests 435 passed (435)'. At b281d1b (everything but the comment-only docblock commit), views/metadata-admin/ + MarketplaceRowActions.installedAppsPins + spec-symbol-parity in --shard=1..4/4: 136/138/138/137 files passed, 1477/1573/1566(+1 skipped)/1704 tests; total 549 files, 6320 passed, 1 skipped. Ablations: fix committed first; each mutation via objectstack scripts/ablation-replace.mjs (anchor hit 1 to 0, blob changed), restored to HEAD blob with git diff HEAD empty. (1) derive Source from writable instead of returning row.source: 4 failed (CONTROL pins in both files + 'a served authored reads as itself'). (2) fall back to 'code' instead of refusing: 12 failed (every absent/unknown pin + 'writable never stands in'). (3) delete the writable refusal: 1 failed (12723 no-verdict pin). Every ablation went red as expected. No build/dist was involved: tests import ./packageSource and ./PackagesPage by relative source path. A first attempt at a non-sharded 677-file sweep was killed by me (own PID) at 20m: NOT MEASURED, superseded by the 4 shards.",
"gates": "Local at 905168f, each exit captured before any pipe: check:new-line-citations 0 ('VERDICT new-cross-file-line-citations: 0 new citation(s)'); check:test-path-roots 0; check:changeset-claims 0 (1 changeset read, no self-contradiction); node scripts/check-changeset-presence.mjs 0 ('14 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'); changeset:check 0; check:i18n-keys 0; check:i18n-drift 0 ('Every changed designer-table en value was followed by its zh row'); check:i18n-dead-keys 0 (report; no new engine.packages candidate); check:i18n-designer-parity 0 ('Every en row has a zh row'); check:spec-symbols 0; check:phantom-deps 0; check:control-bytes 0; eslint on changed files 0 (0 errors, 52 warnings, all present at base: PackagesPage 16 = base 16, marketplaceApi 36 = base 36); app-shell type-check 0. objectui has no dispatch-gates.mjs, so the list is hand-derived from package.json and there is no --ran reconciliation. check:spec-floors NOT MEASURED (release-path gate, needs a whole-repo build). Substitute measurement on a built app-shell: the changed modules' dist .d.ts import only InstalledPackageAtEitherStage from @objectstack/spec/api-assembled, present in the packed 18.0.0-next.2 export list; .js references no new spec symbol. CI on PR head 905168f at report time: Changeset Bump Policy success, Changeset Fixed Group Check success, label success; Lint, Type Check, Test (shard 1..16/16), Test (dist pins), Repo Gates, Build & E2E, Build Docs, Bundle Analysis, Changeset Declaration, Changeset Claim Re-read, Changeset Overwrite Report, Line Citation Gate, Governed Surface Queue Guard, Live E2E (informational) all in_progress; dependabot, Test (coverage) and Test (coverage shard) skipped.",
"mcp_calls": "0",
"api_writes": "2 relay strokes. (1) POST /repos/objectstack-ai/objectstack/dispatches (fleet-write fw-20261018T063652Z-4a0b1d), which executed POST /repos/objectstack-ai/objectui/pulls (draft #12939) + POST /repos//issues/12939/assignees [sunhaolin]; read-back: 10092 bytes sent = stored, assignee listed. (2) POST /repos/objectstack-ai/objectstack/dispatches, which executes POST /repos//issues/12790/comments (this os-dev-report). git push is not counted. No label writes.",
"deviations": [
"Card bullet 'The second read (readMarketplaceInstalls) and the join leave this page': the join left, the read stayed. On main it also feeds the marketplace row actions and the not-loaded install rows, pinned in MarketplaceRowActions.installedAppsPins.test.tsx (objectui#11645/#12610/#12684/#7611). The read now never names a registry row's Source; a marketplace entry rides only a row the server serves as 'marketplace'. Decided by the ruling's 'one list of what is installed' and recorded in open_questions[0].",
"Dispatch premise 'MarketplacePage.tsx reads readMarketplaceInstalls' is false: git grep finds PackagesPage.tsx as its only caller (MarketplacePage reads listLocalInstalls/listInstalledPackages). The function stays in marketplaceApi.ts either way.",
"Clause-2 premise 'reachable from no @object-ui/app-shell export' is false at module-graph level: src/index.ts reaches packageSource.ts via the exported MarketplacePage and PackagesPage.tsx via services/builtinComponents (registered as developer:packages). Neither module's symbols are re-exported, so the export surface is unchanged and the change only narrows acceptance. PR body line kept as 'Clause-②: no'.",
"File surface widened, all comment-only or fixture-only. (a) The refusal copy lives in packages/app-shell/src/views/metadata-admin/i18n.ts (the designer table, en+zh), not packages/i18n/src/locales; edited there. (b) console/marketplace/marketplaceApi.ts: two docblocks that this change made false, comment-only. (c) console/marketplace/tests/MarketplaceRowActions.installedAppsPins.test.tsx: LOADED_ROW gains the served source, per Zone 2 item 3.",
"Added PACKAGE_WRITABLE_UNKNOWN (new local refusal + key engine.packages.scope.unknown), which the card does not name. It keeps the 12723 pin 'a row with no writable verdict is refused at load', which the retired join used to satisfy. Without it a row missing a spec-required key would render.",
"PACKAGE_SOURCES is a test-pinned tuple, not read from InstalledPackageSourceSchema at runtime: the floor ^18.0.0-next.2 lacks that symbol and the pin bump is forbidden. The card allows either form.",
"Did not run app-shell's full suite; ran views/metadata-admin/ (4 shards) plus touched files, as dispatched."
],
"files_changed": [
".changeset/12790-packages-served-source.md",
"packages/app-shell/src/views/metadata-admin/packageSource.ts",
"packages/app-shell/src/views/metadata-admin/packageSource-12790.test.ts",
"packages/app-shell/src/views/metadata-admin/PackagesPage.tsx",
"packages/app-shell/src/views/metadata-admin/i18n.ts",
"packages/app-shell/src/views/metadata-admin/PackagesPage.source-12675.test.tsx",
"packages/app-shell/src/views/metadata-admin/PackagesPage.scopeWritable-12723.test.tsx",
"packages/app-shell/src/views/metadata-admin/PackagesPage.confirmFlows-11784.test.tsx",
"packages/app-shell/src/views/metadata-admin/PackagesPage.walledCatalogRefusal-7611.test.tsx",
"packages/app-shell/src/views/metadata-admin/PackagesPage.duplicateIdRule-11855.test.tsx",
"packages/app-shell/src/views/metadata-admin/PackagesPage.pendingHeaderMarkup-11804.test.tsx",
"packages/app-shell/src/views/metadata-admin/PackagesPage.publishRefusal-11922.test.tsx",
"packages/app-shell/src/views/metadata-admin/EditPackageDialog.test.tsx",
"packages/app-shell/src/console/marketplace/marketplaceApi.ts",
"packages/app-shell/src/console/marketplace/tests/MarketplaceRowActions.installedAppsPins.test.tsx"
],
"line_budget": "n/a",
"open_questions": [
{
"question": "The card says readMarketplaceInstalls leaves the Packages page, but the read also drives the marketplace row actions (Details/Uninstall) and the not-loaded install rows that only this page lists. Which reading does the seat accept?",
"options": [
"A (shipped in #12939): retire the join only. Source reads row.source and nothing else; the read stays solely for the actions and the not-loaded rows, attached only where the server says 'marketplace'.",
"B (the card's letter): delete the read from this page. Details/Uninstall and every not-loaded install row disappear from Setup, together with the objectui#11645/#12610/#12684/#7611 pins. A follow-up would then have to give them a new home (for example a source/notLoaded statement from objectstack, or a marketplace-side list)."
],
"recommendation": "A. Business need: not-loaded installs and their Uninstall are a real operator path. objectstack's 'Installed Apps' nav now opens this page, and B leaves no list of them. Long-term fit: A leaves exactly one Source signal (the producer's), and the marketplace list is demoted to an action carrier, so there is no join. Hard for AI to get wrong: the Source can no longer be derived, and a missing key is refused by name. Startup focus: A adds nothing; B deletes delivered behaviour and needs new surface elsewhere."
},
{
"question": "With this change, a server from before objectstack#24010 gets every Packages row refused. app-shell still declares @objectstack/spec ^18.0.0-next.2, a range that admits a spec, and a paired runtime, without
source. The dispatch forbids a pin bump in this PR.","options": [
"A: raise app-shell's @objectstack/spec floor to ^18.0.0-next.3 in a follow-up, so the declared range is honest about the runtime the page needs.",
"B: leave the floor; the fixed-group release moves objectui and objectstack together and next.2 is a superseded prerelease."
],
"recommendation": "A. Declare equals enforce: the page now requires the declared key, and an honest floor costs one line. It also lets a later change read InstalledPackageSourceSchema at runtime instead of a pinned tuple."
}
],
"out_of_scope_findings": [
"carrier: PR #12939 Acceptance notes · noted, not filed: a cloud-connected environment's control-plane list holds installs made by any path, and a list item with no registry row is still drawn as 'marketplace' (unchanged from objectui#12675). Read-only inference, not reproduced.",
"carrier: PR #12939 Acceptance notes · noted, not filed: while the capability is on, an unreadable marketplace list still refuses the whole Packages list. This is behaviour from before this PR; its reason is now the not-loaded rows and actions rather than the Source."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT → PR #12939 comment
6246795614· head905168f·Clause-②: no· Q1 A accepted, Q2 carried to the round report ·domain:uiseat 1 ·session_0188TsgPV8kQXfuRuKmkj2ks· 2026-10-18T06:55ZThe PR goes to ready and auto-merge through objectui's merge queue, and the landing note follows on MERGED.
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed ·
domain:uiseat 1 ·session_0188TsgPV8kQXfuRuKmkj2ks· 2026-10-18T07:11ZPR #12939 merged through objectui's merge queue as
bc96bc34b5. It has one parent,7bbd718b(PR #12938's merge, which landed while this PR was queued), and is an ancestor oforigin/main.7bbd718bc96bc39b82cedto reviewed head905168f)export function resolvePackageSourceinpackageSource.tsexport function readServedSourceinpackageSource.tsState change:
Fixes #12790closed this cardcompleted, and this act removespm:dispatched. The PR body's only closing keyword names this card.sourcefor its Source column and?source=filter. An absent or unknown value is refused by name. Thewritable× installed-list join andPACKAGE_SOURCE_CONFLICTare gone. The marketplace list stays only for the row actions and the not-loaded installs, per Q1 A in the ACCEPT6246795614.Carried, not filed: Q2.
@object-ui/app-shellstill declares@objectstack/spec^18.0.0-next.2. That range admits a spec, and a paired runtime, that does not declare or servesource, so the page would refuse every row against it. The seat put this to the maintainer in its round report.Records:
6245976532;6246795614.Generated by Claude Code
feat(app-shell): Setup → Packages reads the served `source`; the `wri…