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
platform-objects: Setup's Invitations, API Keys, OAuth Applications and three notification objects declare no view emptyState, so their lists show generic fallbacks; declare each (and API Keys' Create key action), moved from objectui#12607 #23852
Filing gate ③: objectstack-ai/objectui#12607 moved to where its fix lands (rule 1). It was filed by the epic PM from the 2026-10-16 Setup review, under the maintainer's 「全部立卡」. Triage moved it at that card's pm:retriage (domain:ui seat 1, 6211622904), which found no objectui residue. ⛔ Not a claim.
Reader who acts: the domain:engine seat (packages/platform-objects).
Measured (seat 1's reading at objectui 07b0632 and objectstack 5b6c257)
objectui renders an authored empty state first:ObjectView.tsx:3442 uses the view's emptyState??resolveManagedByEmptyState(...), and ListView.tsx:5874-5876 draws schema.emptyState?.title / ?.message. The generic "No identity records…" and "Nothing here yet" appear only because these objects' views declare none.
The spec carries a view-level emptyState:translation.zod.ts:247-272, objects.OBJECT._views.VIEW.emptyState.{title,message}.
sys-invitation.object.ts, sys-api-key.object.ts, sys-oauth-application.object.ts and the three notification objects (preferences, subscriptions, templates) declare a view emptyState with a title and one sentence. A view-filtered empty state names the view, for example "No pending invitations".
API Keys gains a "Create key" primary action, through the server's existing key-creation route that the Account app uses. The claimant confirms the route, and if a new door is needed, that half is its own question.
The copy moves through the translation bundles for every locale platform-objects ships.
The identity fallback stays only for provider-owned objects (Sessions, Accounts). That needs no change: it follows from 1.
Acceptance
Each of the six objects' list renders its own empty title.
Invitations' Pending view renders "No pending invitations".
API Keys renders the Create key action.
The tests sit at the metadata level here. objectui's renderer is already measured to draw them.
Claim: PM loop round 1 · 2026-10-16T11:49Z · on the lane triage's move set (the card names the domain:engine seat as its reader for all six objects)
Session: session_01TQLy3Yq6vF4SDpTXdr3Uwi
Account: os-tesla (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-23852-setup-empty-states
Worktree: objectstack-issue-23852
Domain: domain:engine
Seat: domain:engine#2 (seat post #20966)
File surface: the list views of packages/platform-objects/src/identity/sys-invitation.object.ts, sys-api-key.object.ts and sys-oauth-application.object.ts (an emptyState each; API Keys' "Create key" action); the list views of packages/services/service-messaging/src/objects/notification-preference.object.ts, notification-subscription.object.ts and notification-template.object.ts (an emptyState each; declared to domain:services); both packages' generated translation bundles (the en bundles regenerated, translated leaves hand-written per AGENTS.md); tests in those two packages; one .changeset/23852-setup-empty-states.md. Stop on a breach and explain it in the report.
Container & model: M, mode:subagent, model: default (dispatch-gates --tier: no path-derived mandate)
Clause-②: no
Responsibility: n/a — not a defect card
Thread-read: none
Serial constraints cleared: no open PR touches these six object files (read 2026-10-16T11:49Z). #23838's PR #23869 (in the merge queue) edits the enable: blocks of sys-organization / sys-team / sys-business-unit / sys-user, other files. #23803 (in flight) holds setup-nav.contributions.ts and sys-user.page.ts, other files. #23845 (#23802, merged 30ce56e803) already changed these objects' descriptions; the dev works on the merged code. Same area:identity as #23838, but the file surfaces do not intersect.
The card's own condition on item 2 (API Keys' "Create key"), verbatim: "The claimant confirms the route, and if a new door is needed, that half is its own question." The dev confirms the existing key-creation route the Account app uses; if none fits, item 2 is reported back, not built.
os-dev-report
{
"issue": 23852,
"status": "done",
"branch": "claude/issue-23852-setup-empty-states",
"pr": "#23924",
"session": "session_01TQLy3Yq6vF4SDpTXdr3Uwi — subagent run; the parent PM seat's id, the one the relay envelope stamped on both writes",
"premise_still_valid": true,
"summary": "All three items shipped, so the PR body closes this card. (1) Every list view of sys_invitation, sys_api_key and sys_oauth_application declares an emptyState with a title and one sentence, and each filtered view names itself (Pending reads 'No pending invitations'). The three notification objects had NO list views, which falsifies the claim's 'the list views of'. Each now ships one 'all' view declared only for its emptyState, with columns: [] (the spec's renderer-derived form) and no filter, sort or pagination, so the grid is unchanged except the tab label ('All' instead of the console's 'All records'). (2) sys_api_key.create_api_key ('Create Key', primary list_toolbar) calls the existing POST /api/v1/keys and reveals the raw key in a secret resultDialog. No new door was needed; the route reading is below. (3) The en bundles were regenerated, the zh-CN, ja-JP and es-ES leaves hand-written, and the source-hash files have no net change. The one-shot reveal class guard gained /api/v1/keys, so every action on the mint route must reveal 'key'.",
"route_reading_item2": "POST /api/v1/keys exists on origin/main: packages/runtime/src/domains/keys.ts (createKeysDomains / handleKeysRequest), mounted by server.post(prefix + '/keys') in packages/runtime/src/dispatcher-plugin.ts, route-ledger row 'POST /keys' disposition sdk client keys.create. It is the only mint path. It whitelists name plus an optional expires_at, pins user_id and the active organization to the caller, answers 201 {success, data:{id,name,prefix,key,...}} with the raw key once, stores only the hash, and does not depend on the create affordance, which stays off. The PM grep missed it because the symbols are keys / createKeysDomains. Console callers at objectui origin/main a5bb7ac are the mcp:connect-agent widget (ConnectAgentWidget.tsx) and apps/console developer AgentConnectSection.tsx. The Account app's nav_account_api_keys opens sys_api_key view 'mine', the same object list, which had no create entry; the action now serves Setup and the Account app alike. objectui apiHandler POSTs an absolute type:'api' target with credentials, unwraps {success,data} and resolves resultDialog paths against data, the same mechanism as create_oauth_application. No new server door.",
"pm_assumptions": [
"1 HOLDS for emptyState: grep -c emptyState = 0 in all six files, control sys-member.object.ts = 1. FALSIFIED in part: the three service-messaging notification objects declare no listViews at all, so one 'all' view each was added.",
"2 HOLDS: --write added objects.OBJECT._views.VIEW.emptyState.{title,message} to en. The translated locales received en copies plus source-hash provenance rows. After hand translation a second --write dropped every provenance row, so the source-hashes files have no net diff. check:i18n requires bundle key parity (exit 0). check:i18n-coverage counts untranslated declared strings and found none new (exit 0, 621 baselined).",
"3 HOLDS: the route exists and an object type:'api' action with a resultDialog reaches it (see route_reading_item2). It sits under the names keys / createKeysDomains / handleKeysRequest, not createApiKey / api-keys.",
"4 HOLDS: no test pinned these lists' fallback text, and both full suites stayed green with no existing assertion changed."
],
"tests": "At PR head 8944600, after merging origin/main (2 commits, no overlap with this surface), via os-verify-lock. pnpm --filter @objectstack/platform-objects test: 'Test Files 72 passed (72) / Tests 1128 passed (1128)'. pnpm --filter @objectstack/service-messaging test: 'Test Files 52 passed (52) / Tests 584 passed (584)'. pnpm --filter @objectstack/platform-objects --filter @objectstack/service-messaging typecheck: exit 0, with 'check:test-typecheck: OK'; tsc --listFiles confirms the 3 touched test files are compiled. New: packages/platform-objects/src/identity/setup-list-empty-states.test.ts (20 tests) and packages/services/service-messaging/src/objects/setup-list-empty-states.test.ts (10 tests). Extended: two-factor-one-shot-reveal.test.ts ONE_SHOT_ROUTES gained '/api/v1/keys': ['key']. Ablation via scripts/ablation-replace.mjs (anchor count 1 to 0, blob changed, restore proven by blob == HEAD and an empty git diff HEAD). The subjects are imported from src, so no build or dist leg applies. A1: deleting Pending's emptyState.title made 3 tests red (every-view-declares, the Pending pin, en-bundle equality). A2: deleting the {path:'key'} reveal field made 2 tests red ('SysApiKey.create_api_key (/api/v1/keys) does not reveal key', 'expected [] to deeply equal [ObjectContaining]'). A3: changing notification-template columns: [] to ['topic','channel'] made the no-projection pin red. Every ablation went red (the expected direction) and every restore is proven.",
"gates": "Local subset at 8944600, derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths). Each exit code was captured before any pipe: node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0; node scripts/check-adr-0087-registration.mjs --self-test :: exit 0; node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0; node scripts/check-changeset-no-major.mjs --self-test :: exit 0; node scripts/check-ci-filter-parity.mjs :: exit 0; node scripts/check-closing-keyword-parity.mjs :: exit 0; node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0; node scripts/check-comment-mask-adoption.mjs :: exit 0; node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0; node scripts/check-comment-mask-corpus.mjs :: exit 0; node scripts/check-dts-emitted.mjs --self-test :: exit 0; node scripts/check-empty-changeset.mjs --base origin/main :: exit 0; node scripts/check-empty-changeset.mjs --self-test :: exit 0; node scripts/check-issue-citations.mjs :: exit 0; node scripts/check-keyed-text-bounds.mjs :: exit 0; node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0; node scripts/check-platform-object-tenancy-census.mjs :: exit 0; node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0; node scripts/check-plugin-teardown-shape.mjs :: exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0; node scripts/check-registry-log-declared.mjs :: exit 0; node scripts/check-registry-log-declared.mjs --self-test :: exit 0; node scripts/check-rest-log-spy-declared.mjs :: exit 0; node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0; node scripts/check-system-context-census.mjs :: exit 0; node scripts/check-system-context-census.mjs --self-test :: exit 0; node scripts/check-tenant-audit-census.mjs :: exit 0; node scripts/check-tenant-audit-census.mjs --self-test :: exit 0; node scripts/check-undeclared-dep-imports.mjs :: exit 0; node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0; node scripts/docs-audit/check-affected-docs.mjs :: exit 0; node scripts/docs-audit/check-drift-comment.mjs :: exit 0; node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0; node scripts/release-pending-publish.mjs --self-test :: exit 0; pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0; pnpm check:changeset-gate-self-tests :: exit 0; pnpm check:cross-package-test-inputs :: exit 0; pnpm check:doc-authoring :: exit 0; pnpm check:driver-memory-census :: exit 0; pnpm check:dts-closure :: exit 0; pnpm check:dual-build-cjs-loads :: exit 3; pnpm check:engine-double-contract :: exit 0; pnpm check:gitlink-declared :: exit 0; pnpm check:i18n :: exit 0; pnpm check:i18n-stale-fill :: exit 0; pnpm check:issue-citations :: exit 0; pnpm check:lean-entry-closure :: exit 0; pnpm check:logger-receiver-detach :: exit 0; pnpm check:nul-bytes :: exit 0; pnpm check:objectql-double-limit :: exit 0; pnpm check:objectui-changeset :: exit 0; pnpm check:org-identifier :: exit 0; pnpm check:page-declaration-shape :: exit 0; pnpm check:pm-changeset-deadline-census :: exit 0; pnpm check:published-files :: exit 0; pnpm check:query-options-erasure :: exit 0; pnpm check:refd-timer-probe :: exit 0; pnpm check:slot-lookup :: exit 0; pnpm check:sourcemap-no-sources-content :: exit 0; pnpm check:test-source-alias :: exit 0; pnpm check:tier-file-adoption :: exit 0; pnpm check:type-check-coverage :: exit 0; pnpm check:watch-hint-literal :: exit 0; pnpm check:where-matcher :: exit 0; pnpm check:type-check-debt :: exit 0. Reconciled with --ran: '65 derived famil(ies) accounted for — 64 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. NOT MEASURED: pnpm check:dual-build-cjs-loads, reason PREREQUISITE NOT MET (it reads every package's dist/, and this worktree built only the i18n closure); CI Lint & Repo Gates runs it. Roster families with a roster under one of these paths, run locally: node scripts/check-changeset-fixed.mjs :: exit 0; pnpm check:authz-resolver :: exit 0; pnpm check:error-code-casing :: exit 0; pnpm check:filter-alias-parity :: exit 0. Also pnpm check:i18n-coverage :: exit 0 ('14 config(s), 621 baselined untranslated string(s), none new'). The derivation without --commands was read: 53 artifact-roster families (4 marked under these paths, run above), 11 declared-wide families, 5 path-scheduled CI jobs and the type-check lanes, all left to CI. PR #23924 check-runs at report time on head 8944600: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; all 21 others in_progress (Check Changeset, Type Check · workspace/debt ledger/consumer gates/source gates, Lint · ESLint/PM dispatch-gates/repo & tooling gates/CI, release & engine gates/docs & code guards/scoped ×3, Governed Surface Queue Guard, The card this PR closes must claim this branch, Check PR Size, Check Documentation Links, and the rest). Not waited on, per contract.",
"files_changed": [
".changeset/23852-setup-empty-states.md (+19)",
"packages/platform-objects/src/identity/sys-invitation.object.ts (+18)",
"packages/platform-objects/src/identity/sys-api-key.object.ts (+58)",
"packages/platform-objects/src/identity/sys-oauth-application.object.ts (+16)",
"packages/platform-objects/src/identity/setup-list-empty-states.test.ts (+180, new)",
"packages/platform-objects/src/identity/two-factor-one-shot-reveal.test.ts (+5)",
"packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts (+72/-12 each)",
"packages/services/service-messaging/src/objects/notification-{preference,subscription,template}.object.ts (+21 each)",
"packages/services/service-messaging/src/objects/setup-list-empty-states.test.ts (+101, new)",
"packages/services/service-messaging/src/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts (+27 each)"
],
"line_budget": "18 files, +856/-48 against origin/main (under the 3,000 changed-line bar). Objects +155, tests +286, generated bundles +416/-48, changeset +19. No governed surface touched.",
"deviations": [
"The claim says 'the list views of' the three notification objects, but none existed. One 'all' view each was declared (in the claimed files) with columns: [] and no filter, sort or pagination. Visible side effect: the tab label reads 'All' (translated) instead of objectui's synthesized 'All records'.",
"The action label is 'Create Key' (title case, per this object's 'Revoke API Key' / 'Restore API Key' convention). The card wrote 'Create key'.",
"The Subscriptions empty-state copy departs from objectui#12607's 'Subscribe a role, team or user to a topic'. The object header records that subscriptions are not read at runtime, so the copy describes what a row records and promises no delivery.",
"origin/main was merged once before the PR opened (merge commit 8944600, git's default message, no session trailer). The two incoming commits do not overlap this surface. Both suites, typecheck and the full gate set were re-run on the merge.",
"Commit trailers follow AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude'), not the harness reminder's model-named Co-Authored-By. The PR footer uses AGENTS.md's session-URL form, not the harness's emoji form. AGENTS.md wins on both.",
"Scratch tooling (translation applier, gate runner) lives in the scratchpad only; nothing extra is committed."
],
"mcp_calls": "0 — no MCP GitHub tool called (reads via gh api, writes via the scripts/pm relay)",
"api_writes": "5 — session token: 2x POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay strokes: fw-20261016T125854Z-6782fe for the PR, and this report's stroke). Executed by the relay as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/pulls (draft #23924, body read back 9916/9916 bytes identical), POST /repos//issues/23924/assignees (os-tesla, read back), POST /repos//issues/23852/comments (this report). git push x4 (empty branch probe, WIP, bundles+tests, merge) is not REST. No label writes.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted in PR #23924 Acceptance notes, not filed · sys_oauth_application.listViews.active filters disabled == false while the object's own comment says better-auth writes disabled only when set (possibly NULL on never-toggled rows). Read-only inference, not reproduced, so none of a/b/c applies. Dedupe words: oauth application active view disabled null filter",
"carrier: none · noted, not filed · sys_api_key header docblock says the raw token is returned 'via the auth plugin API', but the mint path is the runtime keys domain (comment drift). Dedupe words: sys_api_key docblock auth plugin mint path",
"carrier: none · noted, not filed · service-messaging ja-JP/es-ES bundles are English echoes for the notification objects' labels and fields (pre-existing). The new empty-state leaves are translated, so those pages mix languages. Dedupe words: service-messaging ja-JP es-ES untranslated notification"
]
}
merged: true, merge commit 8f4260fe66, through the merge queue
origin/main
8f4260fe6 is an ancestor of origin/main; sys-api-key.object.ts there carries create_api_key; notification-template.object.ts carries emptyState; .changeset/23852-setup-empty-states.md is present
queue branch
no gh-readonly-queue/main/*pr-23924* head remains
State change: this card closed completed by Fixes #23852; pm:dispatched removed in this act. No other card was closed by this PR. #23860 closed in the same second, by its own PR #23930 (Fixes #23860), merged in the same queue batch.
Carried: the three out-of-scope notes stay in the ACCEPT 6216530486 (the sys_oauth_applicationactive view's NULL reading, the sys_api_key docblock, service-messaging's English echoes in ja-JP / es-ES). No card: none is a measured defect.
Activity
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 1 · 2026-10-16T11:49Z · on the lane triage's move set (the card names the
domain:engineseat as its reader for all six objects)Session:
session_01TQLy3Yq6vF4SDpTXdr3UwiAccount:
os-tesla(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23852-setup-empty-statesWorktree:
objectstack-issue-23852Domain:
domain:engineSeat:
domain:engine#2(seat post #20966)File surface: the list views of
packages/platform-objects/src/identity/sys-invitation.object.ts,sys-api-key.object.tsandsys-oauth-application.object.ts(anemptyStateeach; API Keys' "Create key" action); the list views ofpackages/services/service-messaging/src/objects/notification-preference.object.ts,notification-subscription.object.tsandnotification-template.object.ts(anemptyStateeach; declared todomain:services); both packages' generated translation bundles (theenbundles regenerated, translated leaves hand-written per AGENTS.md); tests in those two packages; one.changeset/23852-setup-empty-states.md. Stop on a breach and explain it in the report.Container & model:
M,mode:subagent,model: default(dispatch-gates --tier: no path-derived mandate)Clause-②: no
Responsibility: n/a — not a defect card
Thread-read: none
Serial constraints cleared: no open PR touches these six object files (read 2026-10-16T11:49Z). #23838's PR #23869 (in the merge queue) edits the
enable:blocks ofsys-organization/sys-team/sys-business-unit/sys-user, other files. #23803 (in flight) holdssetup-nav.contributions.tsandsys-user.page.ts, other files. #23845 (#23802, merged30ce56e803) already changed these objects' descriptions; the dev works on the merged code. Samearea:identityas #23838, but the file surfaces do not intersect.The card's own condition on item 2 (API Keys' "Create key"), verbatim: "The claimant confirms the route, and if a new door is needed, that half is its own question." The dev confirms the existing key-creation route the Account app uses; if none fits, item 2 is reported back, not built.
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23852,
"status": "done",
"branch": "claude/issue-23852-setup-empty-states",
"pr": "#23924",
"session": "session_01TQLy3Yq6vF4SDpTXdr3Uwi — subagent run; the parent PM seat's id, the one the relay envelope stamped on both writes",
"premise_still_valid": true,
"summary": "All three items shipped, so the PR body closes this card. (1) Every list view of sys_invitation, sys_api_key and sys_oauth_application declares an emptyState with a title and one sentence, and each filtered view names itself (Pending reads 'No pending invitations'). The three notification objects had NO list views, which falsifies the claim's 'the list views of'. Each now ships one 'all' view declared only for its emptyState, with columns: [] (the spec's renderer-derived form) and no filter, sort or pagination, so the grid is unchanged except the tab label ('All' instead of the console's 'All records'). (2) sys_api_key.create_api_key ('Create Key', primary list_toolbar) calls the existing POST /api/v1/keys and reveals the raw key in a secret resultDialog. No new door was needed; the route reading is below. (3) The en bundles were regenerated, the zh-CN, ja-JP and es-ES leaves hand-written, and the source-hash files have no net change. The one-shot reveal class guard gained /api/v1/keys, so every action on the mint route must reveal 'key'.",
"route_reading_item2": "POST /api/v1/keys exists on origin/main: packages/runtime/src/domains/keys.ts (createKeysDomains / handleKeysRequest), mounted by server.post(prefix + '/keys') in packages/runtime/src/dispatcher-plugin.ts, route-ledger row 'POST /keys' disposition sdk client keys.create. It is the only mint path. It whitelists name plus an optional expires_at, pins user_id and the active organization to the caller, answers 201 {success, data:{id,name,prefix,key,...}} with the raw key once, stores only the hash, and does not depend on the create affordance, which stays off. The PM grep missed it because the symbols are keys / createKeysDomains. Console callers at objectui origin/main a5bb7ac are the mcp:connect-agent widget (ConnectAgentWidget.tsx) and apps/console developer AgentConnectSection.tsx. The Account app's nav_account_api_keys opens sys_api_key view 'mine', the same object list, which had no create entry; the action now serves Setup and the Account app alike. objectui apiHandler POSTs an absolute type:'api' target with credentials, unwraps {success,data} and resolves resultDialog paths against data, the same mechanism as create_oauth_application. No new server door.",
"pm_assumptions": [
"1 HOLDS for emptyState: grep -c emptyState = 0 in all six files, control sys-member.object.ts = 1. FALSIFIED in part: the three service-messaging notification objects declare no listViews at all, so one 'all' view each was added.",
"2 HOLDS: --write added objects.OBJECT._views.VIEW.emptyState.{title,message} to en. The translated locales received en copies plus source-hash provenance rows. After hand translation a second --write dropped every provenance row, so the source-hashes files have no net diff. check:i18n requires bundle key parity (exit 0). check:i18n-coverage counts untranslated declared strings and found none new (exit 0, 621 baselined).",
"3 HOLDS: the route exists and an object type:'api' action with a resultDialog reaches it (see route_reading_item2). It sits under the names keys / createKeysDomains / handleKeysRequest, not createApiKey / api-keys.",
"4 HOLDS: no test pinned these lists' fallback text, and both full suites stayed green with no existing assertion changed."
],
"tests": "At PR head 8944600, after merging origin/main (2 commits, no overlap with this surface), via os-verify-lock. pnpm --filter @objectstack/platform-objects test: 'Test Files 72 passed (72) / Tests 1128 passed (1128)'. pnpm --filter @objectstack/service-messaging test: 'Test Files 52 passed (52) / Tests 584 passed (584)'. pnpm --filter @objectstack/platform-objects --filter @objectstack/service-messaging typecheck: exit 0, with 'check:test-typecheck: OK'; tsc --listFiles confirms the 3 touched test files are compiled. New: packages/platform-objects/src/identity/setup-list-empty-states.test.ts (20 tests) and packages/services/service-messaging/src/objects/setup-list-empty-states.test.ts (10 tests). Extended: two-factor-one-shot-reveal.test.ts ONE_SHOT_ROUTES gained '/api/v1/keys': ['key']. Ablation via scripts/ablation-replace.mjs (anchor count 1 to 0, blob changed, restore proven by blob == HEAD and an empty git diff HEAD). The subjects are imported from src, so no build or dist leg applies. A1: deleting Pending's emptyState.title made 3 tests red (every-view-declares, the Pending pin, en-bundle equality). A2: deleting the {path:'key'} reveal field made 2 tests red ('SysApiKey.create_api_key (/api/v1/keys) does not reveal key', 'expected [] to deeply equal [ObjectContaining]'). A3: changing notification-template columns: [] to ['topic','channel'] made the no-projection pin red. Every ablation went red (the expected direction) and every restore is proven.",
"gates": "Local subset at 8944600, derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths). Each exit code was captured before any pipe: node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0; node scripts/check-adr-0087-registration.mjs --self-test :: exit 0; node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0; node scripts/check-changeset-no-major.mjs --self-test :: exit 0; node scripts/check-ci-filter-parity.mjs :: exit 0; node scripts/check-closing-keyword-parity.mjs :: exit 0; node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0; node scripts/check-comment-mask-adoption.mjs :: exit 0; node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0; node scripts/check-comment-mask-corpus.mjs :: exit 0; node scripts/check-dts-emitted.mjs --self-test :: exit 0; node scripts/check-empty-changeset.mjs --base origin/main :: exit 0; node scripts/check-empty-changeset.mjs --self-test :: exit 0; node scripts/check-issue-citations.mjs :: exit 0; node scripts/check-keyed-text-bounds.mjs :: exit 0; node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0; node scripts/check-platform-object-tenancy-census.mjs :: exit 0; node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0; node scripts/check-plugin-teardown-shape.mjs :: exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0; node scripts/check-registry-log-declared.mjs :: exit 0; node scripts/check-registry-log-declared.mjs --self-test :: exit 0; node scripts/check-rest-log-spy-declared.mjs :: exit 0; node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0; node scripts/check-system-context-census.mjs :: exit 0; node scripts/check-system-context-census.mjs --self-test :: exit 0; node scripts/check-tenant-audit-census.mjs :: exit 0; node scripts/check-tenant-audit-census.mjs --self-test :: exit 0; node scripts/check-undeclared-dep-imports.mjs :: exit 0; node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0; node scripts/docs-audit/check-affected-docs.mjs :: exit 0; node scripts/docs-audit/check-drift-comment.mjs :: exit 0; node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0; node scripts/release-pending-publish.mjs --self-test :: exit 0; pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0; pnpm check:changeset-gate-self-tests :: exit 0; pnpm check:cross-package-test-inputs :: exit 0; pnpm check:doc-authoring :: exit 0; pnpm check:driver-memory-census :: exit 0; pnpm check:dts-closure :: exit 0; pnpm check:dual-build-cjs-loads :: exit 3; pnpm check:engine-double-contract :: exit 0; pnpm check:gitlink-declared :: exit 0; pnpm check:i18n :: exit 0; pnpm check:i18n-stale-fill :: exit 0; pnpm check:issue-citations :: exit 0; pnpm check:lean-entry-closure :: exit 0; pnpm check:logger-receiver-detach :: exit 0; pnpm check:nul-bytes :: exit 0; pnpm check:objectql-double-limit :: exit 0; pnpm check:objectui-changeset :: exit 0; pnpm check:org-identifier :: exit 0; pnpm check:page-declaration-shape :: exit 0; pnpm check:pm-changeset-deadline-census :: exit 0; pnpm check:published-files :: exit 0; pnpm check:query-options-erasure :: exit 0; pnpm check:refd-timer-probe :: exit 0; pnpm check:slot-lookup :: exit 0; pnpm check:sourcemap-no-sources-content :: exit 0; pnpm check:test-source-alias :: exit 0; pnpm check:tier-file-adoption :: exit 0; pnpm check:type-check-coverage :: exit 0; pnpm check:watch-hint-literal :: exit 0; pnpm check:where-matcher :: exit 0; pnpm check:type-check-debt :: exit 0. Reconciled with --ran: '65 derived famil(ies) accounted for — 64 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. NOT MEASURED: pnpm check:dual-build-cjs-loads, reason PREREQUISITE NOT MET (it reads every package's dist/, and this worktree built only the i18n closure); CI Lint & Repo Gates runs it. Roster families with a roster under one of these paths, run locally: node scripts/check-changeset-fixed.mjs :: exit 0; pnpm check:authz-resolver :: exit 0; pnpm check:error-code-casing :: exit 0; pnpm check:filter-alias-parity :: exit 0. Also pnpm check:i18n-coverage :: exit 0 ('14 config(s), 621 baselined untranslated string(s), none new'). The derivation without --commands was read: 53 artifact-roster families (4 marked under these paths, run above), 11 declared-wide families, 5 path-scheduled CI jobs and the type-check lanes, all left to CI. PR #23924 check-runs at report time on head 8944600: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; all 21 others in_progress (Check Changeset, Type Check · workspace/debt ledger/consumer gates/source gates, Lint · ESLint/PM dispatch-gates/repo & tooling gates/CI, release & engine gates/docs & code guards/scoped ×3, Governed Surface Queue Guard, The card this PR closes must claim this branch, Check PR Size, Check Documentation Links, and the rest). Not waited on, per contract.",
"files_changed": [
".changeset/23852-setup-empty-states.md (+19)",
"packages/platform-objects/src/identity/sys-invitation.object.ts (+18)",
"packages/platform-objects/src/identity/sys-api-key.object.ts (+58)",
"packages/platform-objects/src/identity/sys-oauth-application.object.ts (+16)",
"packages/platform-objects/src/identity/setup-list-empty-states.test.ts (+180, new)",
"packages/platform-objects/src/identity/two-factor-one-shot-reveal.test.ts (+5)",
"packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts (+72/-12 each)",
"packages/services/service-messaging/src/objects/notification-{preference,subscription,template}.object.ts (+21 each)",
"packages/services/service-messaging/src/objects/setup-list-empty-states.test.ts (+101, new)",
"packages/services/service-messaging/src/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts (+27 each)"
],
"line_budget": "18 files, +856/-48 against origin/main (under the 3,000 changed-line bar). Objects +155, tests +286, generated bundles +416/-48, changeset +19. No governed surface touched.",
"deviations": [
"The claim says 'the list views of' the three notification objects, but none existed. One 'all' view each was declared (in the claimed files) with columns: [] and no filter, sort or pagination. Visible side effect: the tab label reads 'All' (translated) instead of objectui's synthesized 'All records'.",
"The action label is 'Create Key' (title case, per this object's 'Revoke API Key' / 'Restore API Key' convention). The card wrote 'Create key'.",
"The Subscriptions empty-state copy departs from objectui#12607's 'Subscribe a role, team or user to a topic'. The object header records that subscriptions are not read at runtime, so the copy describes what a row records and promises no delivery.",
"origin/main was merged once before the PR opened (merge commit 8944600, git's default message, no session trailer). The two incoming commits do not overlap this surface. Both suites, typecheck and the full gate set were re-run on the merge.",
"Commit trailers follow AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude'), not the harness reminder's model-named Co-Authored-By. The PR footer uses AGENTS.md's session-URL form, not the harness's emoji form. AGENTS.md wins on both.",
"Scratch tooling (translation applier, gate runner) lives in the scratchpad only; nothing extra is committed."
],
"mcp_calls": "0 — no MCP GitHub tool called (reads via gh api, writes via the scripts/pm relay)",
"api_writes": "5 — session token: 2x POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay strokes: fw-20261016T125854Z-6782fe for the PR, and this report's stroke). Executed by the relay as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/pulls (draft #23924, body read back 9916/9916 bytes identical), POST /repos//issues/23924/assignees (os-tesla, read back), POST /repos//issues/23852/comments (this report). git push x4 (empty branch probe, WIP, bundles+tests, merge) is not REST. No label writes.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted in PR #23924 Acceptance notes, not filed · sys_oauth_application.listViews.active filters disabled == false while the object's own comment says better-auth writes disabled only when set (possibly NULL on never-toggled rows). Read-only inference, not reproduced, so none of a/b/c applies. Dedupe words: oauth application active view disabled null filter",
"carrier: none · noted, not filed · sys_api_key header docblock says the raw token is returned 'via the auth plugin API', but the mint path is the runtime keys domain (comment drift). Dedupe words: sys_api_key docblock auth plugin mint path",
"carrier: none · noted, not filed · service-messaging ja-JP/es-ES bundles are English echoes for the notification objects' labels and fields (pre-existing). The new empty-state leaves are translated, so those pages mix languages. Dedupe words: service-messaging ja-JP es-ES untranslated notification"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23924 comment 6216530486 ·
domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T13:27Z. Landing next: ready, then auto-merge through the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landing record: PR #23924 →
8f4260fe66·domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T13:57Zmerged: true, merge commit8f4260fe66, through the merge queueorigin/main8f4260fe6is an ancestor oforigin/main;sys-api-key.object.tsthere carriescreate_api_key;notification-template.object.tscarriesemptyState;.changeset/23852-setup-empty-states.mdis presentgh-readonly-queue/main/*pr-23924*head remainsState change: this card closed
completedbyFixes #23852;pm:dispatchedremoved in this act. No other card was closed by this PR. #23860 closed in the same second, by its own PR #23930 (Fixes #23860), merged in the same queue batch.Carried: the three out-of-scope notes stay in the ACCEPT 6216530486 (the
sys_oauth_applicationactiveview's NULL reading, thesys_api_keydocblock,service-messaging's English echoes inja-JP/es-ES). No card: none is a measured defect.Generated by Claude Code