Skip to content

refactor(console): re-key the API Console storage catalog to the canonical storage slot - #10498

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-5291-api-console-storage-slot
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-5291-api-console-storage-slot

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #5291

Clause-②: no — a console-internal catalog is re-keyed to the spec's canonical slot name. No library export, schema or accept set moves.

Dispatched by the domain:ui seat 2 PM loop, session https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN (os-dev, mode:subagent).

What changed

  • apps/console/src/pages/developer/hooks/useApiDiscovery.ts: the Storage entry of SERVICE_ENDPOINT_CATALOG is keyed storage, the canonical CoreServiceName slot, and no longer the deprecated file-storage. DEPRECATED_SERVICE_SLOT_ALIASES and the special-cased lookup in discover() are removed. The lookup is discoveredServices[serviceName] again, so the page reads the canonical key and nothing else.
  • useApiDiscovery.test.ts:
    • The tripwire now pins SERVICE_ENDPOINT_CATALOG.storage as defined, SERVICE_ENDPOINT_CATALOG['file-storage'] as undefined, and storage as a CoreServiceName member.
    • The alias-table case is deleted.
    • The deprecated-alias case is inverted: a file-storage row alone renders no group.
    • Two cases are added. The v17 mirror shape (both keys, byte-equal) renders one Storage group. routes.storage is used when the service row carries no route.
    • The fail-closed cases are re-keyed to storage.
  • .changeset/5291-api-console-storage-slot.md: @object-ui/console patch.

Premise check (base cdefa2a06, the origin/main this branch was cut from)

  • H1 (restart condition), held. The card's own probe exits 0 from the worktree root and from apps/console:
    • probe: node -e "process.exit(require('@objectstack/spec/system').CoreServiceName.options.includes('storage')?0:1)";
    • resolved package: @objectstack/spec@17.4.0;
    • CoreServiceName.options lists both storage and file-storage;
    • on base the catalog was still keyed 'file-storage' and the alias table was present.
  • H2 (what a 17.4.0 server announces): both keys.
    • The installed spec's own CHANGELOG.md (entry 593c4bf, under its 17.1.0 heading) says: "Discovery reports the service under the canonical storage key and mirrors the row verbatim under the file-storage key for the alias's v17 lifetime".
    • objectstack origin/main corroborates this in both producers. packages/runtime/src/http-dispatcher.ts builds the storage and 'file-storage' entries from the same svcAvailable(routes.storage, ...) expression. packages/metadata-protocol/src/protocol.ts assigns services['file-storage'] = { ...services['storage'] }.
    • So removing the alias drops the Storage group on no backend that carries this mirror. The one boundary is under Acceptance notes.
  • H3 (other readers): none.
    • git grep -n -E "file-storage|fileStorage|file_storage" over the whole tree, excluding CHANGELOG.md files and the lockfile, hits only the two touched files.
    • Control, same channel: git grep -c DEPRECATED_SERVICE_SLOT_ALIASES -- apps packages hits both files on base.
    • useApiDiscovery has one importer, ApiConsolePage.tsx, which uses only the hook.

Verification

The final head is b609d15dc. The changeset commit on top of 3e11bd3cf touches no code, so the touched test file has the same bytes at both commits.

Red on base, green on head: the touched test file.

  • Head: pnpm exec vitest run apps/console/src/pages/developer/hooks/useApiDiscovery.test.ts printed Tests 14 passed (14). The lock printed VERDICT command-exit 0.

  • Base leg: the source file was replaced on disk by the base blob, and the new test file was kept.

    • The disk git hash-object equalled the base blob 0d878ff02.
    • DEPRECATED_SERVICE_SLOT_ALIASES went from 0 to 4 occurrences, and the canonical-key line storage: { went from 1 to 0.
    • vitest printed Tests 3 failed | 11 passed (14) and exited 1.
    • Restore: git checkout HEAD -- on that path, with a trap. The disk blob then equalled the HEAD blob 18ed1d6c8, and git diff HEAD was empty.

    The three cases that failed on base:

    • does not read the deprecated file-storage alias: "expected { key: 'Storage', ... } to be undefined".
    • falls back to routes.storage when the service row advertises no route: got /api/v1/storage/... instead of /api/v2/files/.... On base the routes map was indexed with file-storage, a key ApiRoutesSchema does not have.
    • the storage group is keyed by the canonical slot, never by the deprecated alias: "catalog must be keyed by the canonical storage slot: expected undefined to be defined".
  • Green on both, by design:

    • The mirror-shape case is a control for the real producer shape.
    • SLOTS.has('storage') asserts the installed spec, not the source.
    • The fail-closed cases pass on both sides because base read the canonical key first.

Package suite: pnpm exec vitest run --maxWorkers=2 apps/console/ at b609d15dc printed Test Files 116 passed (116) and Tests 1270 passed (1270). The lock printed VERDICT command-exit 0. The 116 files equal the tracked test-file count under apps/console, which includes the touched one.

Typecheck:

  • turbo run build --filter="@object-ui/console^..." --concurrency=2 printed 34 successful, 34 total.
  • pnpm --filter @object-ui/console type-check (both tsc legs) exited 0 with 0 error TS, and the lock printed VERDICT command-exit 0.
  • tsc --noEmit --listFilesOnly in apps/console lists both touched files, among 3793 files in the program.

Lint: pnpm --filter @object-ui/console exec eslint . --format json covered 220 files with 0 errors. The warnings are pre-existing no-explicit-any / set-state-in-effect; the touched test file has 0.

Gates. The base is pinned with --base cdefa2a06 / OS_CHANGESET_BASE. Each verdict line is quoted.

Gate Verdict
check-changeset-presence 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/5291-api-console-storage-slot.md.
check-changeset-claims No pending changeset names a file this change touches.
check-changeset-no-major No changeset declares a major bump.
check-changeset-overwrite No pre-existing changeset was modified or deleted.
check-changeset-fixed All workspace packages are in the changeset fixed group.
check:new-line-citations VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0
check:control-bytes check-control-bytes: OK
check:i18n-drift No en value changed in this range.
check:spec-symbols nothing cites a key its spec symbol does not declare.
check:unreferenced-sources OK Every shipped source file in every covered package is reachable.
check:test-path-roots check-test-path-roots: OK
check:vi-mock-specifiers check-vi-mock-specifiers: OK
check:vi-mock-inherit check-vi-mock-inherit: OK
check:vi-mock-override-shape check-vi-mock-override-shape: OK
check:installed-pin-claims OK
check:pending-changeset-literals No test source names a pending changeset.

NOT MEASURED: check:readme-exports. Reason: prerequisite not met locally, because it needs every package's dist and printed "543 self-import(s) could not be judged ... run pnpm build first". Its population is packages/*/README.md against package type entries, and this diff touches neither. CI owns it.

Acceptance notes

  • Pre-mirror backends. A backend older than the framework#9683 discovery mirror reports this slot only as file-storage. Against such a backend the API Console now hides the Storage group, fail-closed like any slot absent from /discovery. Every backend that carries the mirror renders the same group as before. The card and its re-queue scope the retirement of the console-side alias to now, not staged. The changeset states this behaviour change.
  • Stale comments in objectstack. Both objectstack mirror sites (http-dispatcher.ts and protocol.ts) have comments that name objectui's console endpoint catalog as a reader of the file-storage key. Once this lands, that is no longer true. This is comment drift with no behavioural consequence. Noted, not filed. Carrier: whoever retires the alias at the next major.

Generated by Claude Code

…nical `storage` slot

`@objectstack/spec` now publishes `storage` in `CoreServiceName`, so the
service-gated endpoint catalog is keyed by the canonical slot. The
console-side alias table and the special-cased lookup in `discover()` are
removed: the page reads `services.storage` and nothing else. The tripwire
block in `useApiDiscovery.test.ts` now pins the canonical key and pins the
deprecated `file-storage` alias as never read.

Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3045.2 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-Db_JBvps.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.59KB 130.15KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.86KB 62.25KB
fields (index.js) 256.53KB 64.97KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.22KB 14.56KB
plugin-charts (index.js) 74.76KB 20.82KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.59KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 231.81KB 61.27KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 168.25KB 41.46KB
plugin-grid (index.js) 215.43KB 58.90KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.48KB 28.29KB
plugin-map (index.js) 21.83KB 7.07KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.90KB 21.97KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b609d15dc40578e455fe485068b14366e525e7b3

Branch check: git rev-parse refs/review/pr-10498 = b609d15dc40578e455fe485068b14366e525e7b3 (matches; branch has not moved). Merge-base with main = cdefa2a06 (the base the PR body names). 2 commits, 3 files: .changeset/5291-api-console-storage-slot.md (+24), useApiDiscovery.test.ts (+61 −77), useApiDiscovery.ts (+16 −49). PR is draft.

① Derived judgments

Which released objectstack versions announce file-storage only. Verified from npm tarballs in scratch, not from the shallow objectstack checkout (283 commits, oldest 2026-09-21, so git log -S there cannot date the mirror). @objectstack/runtime@17.0.0 (published 2026-08-14) dist/index.js line 8437 emits only "file-storage": filesRegistered ? svcAvailable(routes.storage, ...); no storage: services row (grep count 0). @objectstack/metadata-protocol@17.0.0 has only "file-storage": { route: "/api/v1/storage" } and no services["file-storage"] = ... mirror. From 17.1.0 (2026-08-20) both producers announce both: runtime lines 8909–8910 build storage and "file-storage" from the identical svcAvailable(routes.storage, void 0, filesSvc) expression; metadata-protocol line 3626 is services["file-storage"] = { ...services["storage"] }. So the pre-mirror set is exactly 17.0.0 GA plus the seven 17.0.0-rc.x prereleases; every 17.1.0–17.4.0 release carries the mirror. Spec: CoreServiceName options extracted from each published dist/system/index.js: 17.0.0 has 15 members without storage; 17.1.0–17.4.0 have 16 with both storage and file-storage. CHANGELOG entry 593c4bf verified in the 17.4.0 tarball at line 14515, under the ## 17.1.0 heading (line 12614), text as the dev quoted; it also states the alias "retires through the standard retirement flow at the next major", so the framework mirror does not depend on objectui reading it.

Does the console still support 17.0.0 backends. No documented server-version floor exists: git grep -i compatib over apps/console/README.md, packages/*/README.md, docs finds nothing on backend versions; the console gates on no discovery version. The only documented posture is AGENTS.md's dependency-major rule ("major 相同即兼容"). Declared ranges on main: console @objectstack/spec ^17.0.0 (dev), @objectstack/client ^17.3.0; published deps it bundles already sit higher (@object-ui/types ^17.4.0, app-shell ^17.3.0, core ^17.3.0, data-objectstack client ^17.3.0); lock resolves @objectstack/spec@17.4.0. Flag, not a stopper: after this PR the console's own ^17.0.0 range is behaviourally false (its test now asserts SLOTS.has('storage'), false at 17.0.0), and check:spec-floors (scripts/check-spec-range-floors.mjs) cannot see it because it judges spec-symbol references in published artifacts only, so the dev's "not reached" is accurate. The PR does not raise the floor.

Stop condition versus the no-staged-window posture. Read literally, the stop condition fires: a released GA server (runtime@17.0.0) announces file-storage without storage. The dev proceeded on a narrowed reading ("a server built on the installed 17.4.0") and declared it. Against that: the card's ordered deliverable is itself "delete DEPRECATED_SERVICE_SLOT_ALIASES and the special-cased lookup", which is the fallback; under the wide reading the card could never be done while 17.0.0 exists on npm, contradicting the maintainer's 「5291 放回队列」. The 2026-08-27 ruling is real and standing: objectstack .changeset/15932-...md:71, 19116-...md:23, 19580-...md:137 each carry "No deprecation window (maintainer 2026-08-27: 「项目在创业阶段,用户也很少,短期不考虑渐进」)", and objectstack .claude/skills/pm-dispatch/SKILL.md:253 codifies "废弃别名/拼写与能力退役默认立即退休,无分阶段". Dropping a 6-day-old, four-minors-superseded 17.0.0 is consistent with that. What is wrong: triage 5805869684's premise "无用户可见变化,纯清理" is false for 17.0.0 hosts (the changeset correctly says so), and the implementer, not the seat, resolved the reading. The seat must record acceptance of the 17.0.0 drop; no code change follows from it.

routes.storage fallback. Correct. ApiRoutesSchema (src/api/discovery.zod.ts) has storage: z.string().optional() in both 17.0.0 (line 176) and 17.4.0 (line 280); it never had file-storage. metadata-protocol@17.0.0 line 3270 maps service "file-storage" to route key "storage". Head code: serviceInfo?.route ?? discoveredRoutes[serviceName] ?? catalog.defaultRoute with serviceName = 'storage'. Practically inert: both producers always set route on a usable row, and an unregistered row is svcUnavailable and never reaches the prefix.

Tests, read not run. Head file: 11 it( lines, one it.each over 4 entries, so 14 cases, matching the claimed 14. Red on base by reading: (1) tripwire SERVICE_ENDPOINT_CATALOG.storage toBeDefined is undefined on base (keyed 'file-storage'); (2) does not read the deprecated file-storage alias gives {'file-storage': USABLE}, base resolves discoveredServices['storage'] ?? discoveredServices['file-storage'] to USABLE and renders, so toBeUndefined fails; (3) falls back to routes.storage gives a usable row without route and routes: {storage: '/api/v2/files'}, base indexes discoveredRoutes['file-storage'] and lands on /api/v1/storage. Exactly 3, as claimed. Mirror-shape control is a real producer shape: svcAvailable returns {enabled: true, status, handlerReady, route, provider} (objectstack http-dispatcher.ts line 1672–1681), which is the test's USABLE_STORAGE field set, and both producers emit byte-equal rows from 17.1.0. The every-key-is-a-slot tripwire holds: head catalog keys ai, realtime, notification, analytics, automation, ui, storage are all in the 17.4.0 enum.

Other readers. git grep -n -E "file-storage|fileStorage|file_storage|DEPRECATED_SERVICE_SLOT_ALIASES" refs/review/pr-10498 -- apps packages excluding CHANGELOG: 8 hits, all in the two touched files (6 test, 2 source comments), none a reader; DEPRECATED_SERVICE_SLOT_ALIASES 0 hits at head. Whole tree adds only the changeset.

② Semver level

@object-ui/console is released: in .changeset/config.json fixed group, absent from ignore, publishConfig.access: public, version 17.6.0, no private; check-changeset-presence.mjs header calls it "the single most-edited published package". So a bump level is legitimate and an empty frontmatter would have been wrong given a behaviour change. patch is defensible: the removed export is not on the package's exports map (only ./plugin.js), and no documented server floor is broken. AGENTS.md line 271 says objectui breaking changes take minor; the only mechanical gate ("Changeset Bump Policy" runs check-changeset-no-major.mjs) does not adjudicate patch-versus-minor. Judgment call, patch acceptable; minor would be the conservative choice.

Changeset prose, sentence by sentence: keyed by storage and reads only that slot, true (head discoveredServices[serviceName]); direct lookup and fail-closed per ADR-0076 D12, true (code comment and usable = serviceInfo ? isServiceUsable(serviceInfo) : false); #5286 kept file-storage with an alias table reading storage first, true (base diff); spec now lists storage, true (17.1.0 onward, lock at 17.4.0); mirror-carrying releases render the same group, true for 17.1.0–17.4.0; older backend hides the group, true (17.0.0 verified); routes.storage now supplies the prefix and file-storage never matched the route key, true (ApiRoutesSchema). No cross-file path:line citations in the changeset, source, tests, or commit messages.

③ Boundary flags

  • Stop condition, declared deviation. Literal text fires on runtime@17.0.0; dev proceeded on a narrowed reading. Substance consistent with the 2026-08-27 ruling and the card's deliverable; the seat, not the dev, must record the acceptance. Triage's "no user-visible change" premise is false for 17.0.0 hosts.
  • Patch changeset rather than empty. Correct direction; see ②.
  • Acceptance-note carrier as a role. os-dev.md line 50: carrier: is the PR or person who will touch the file, else 「承接者:无」. "whoever retires the alias at the next major" is neither. Also the report's out_of_scope_findings is [] while the PR names a carrier; the contract puts a carried finding in that array. Form only; the dev's offered fix ("carrier none") is right.
  • Comment drift at objectstack mirror sites. Confirmed at objectstack origin/main aa04ea2: http-dispatcher.ts lines 2063–2068 and protocol.ts lines 6201–6208 name "objectui's console endpoint catalog" as the reader of the file-storage key; false once this lands. Not class a/b/c under os-dev.md lines 41–45, so "noted, not filed" is per contract; harmless because 593c4bf ties the alias lifetime to the next major, not to this reader.
  • Floor honesty. Console's @objectstack/spec ^17.0.0 is now false for its own test suite and for the page's wire behaviour; effective floor is 17.4.0 via @object-ui/types. Not gated, not raised. Follow-up material, not a stopper.
  • Clause-②: no is correct. objectstack execution-duties.md:66 criterion is "放宽接受集或扩大公开面吗"; this narrows the accept set and touches no published contract surface.
  • Commit trailers use the role-file pair (Claude-Session plus Co-authored-by: Claude), declared; repo rule takes precedence over the harness reminder.
  • CI state, read only, second read at 2026-09-25T05:27:37Z: 42 check-runs, 36 success, 3 skipped (dependabot, coverage, coverage shard — expected), 3 still in_progress: Test (shard 4/8), Lint, Spec Main Shape Gate. Type Check and shards 1–3, 5–8 concluded success. Nothing has failed. Not landable until the three conclude.
  • Nothing found in the code that should stop landing. Discovery lookup, route resolution, tripwire, and the fail-closed control are correct against spec 17.4.0 and both producers at 17.1.0 and later.

Implemented-by: claude/issue-5291-api-console-storage-slot
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 05:34
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 00715f5 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-5291-api-console-storage-slot branch September 25, 2026 05:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-key the API Console storage catalog to storage once @objectstack/spec publishes the canonical slot

2 participants