Skip to content

meta read: a view stored with no label is served with label = its qualified name (recheck_gate.default), invented by the read-side translator, so the console view tab shows the machine name #23967

Closed

Description

@objectstack-fleet

Filing gate ①: a user-visible defect with a named landing and a live reproduction. A machine name is shown where the platform's own code says no label was authored.

QA-source: #23942 · studio-authoring · observation (cloud slice comment on #23878; re-checked and filed under #23878)

What happens

The spec declares label optional on a view (packages/spec/src/ui/view.zod.ts:5154, I18nLabelSchema.optional()), with no default. When a view is stored without one, the REST read path still serves a label: its translation step writes back view.label ?? view.name, so the document carries label: "OBJ.default". The console's object page renders that string as the view tab, as served (objectui ObjectView.tsx viewTabLabel), so the user sees recheck_gate.default. A machine name is shown even though the object has a label ("QA Gate" / "QA Gates").

The REST layer already knows this fallback is not a label. For view-container slots it drops it explicitly (meta-item-read-gate.ts:3005-3008: "A label that equals input.name where input had none is the translator's fallback, not a translation, and is dropped"). The single-item read and the list read keep it. A consumer therefore cannot tell an authored label from an invented one without comparing it to name, which is the tolerant-consumer shape PD #12 forbids.

Reach — measured, 2/2 fresh loads

Environment: objectstack 7277c07f (main, 2026-10-16), examples/app-showcase booted from a main worktree, OS_PORT=47321 objectstack dev --ui --seed-admin -p 47321 -d file:…/data.db. Console: objectui daeb77fc (the .objectui-sha pin), built against @objectstack/spec from a35f7a67^ (34b740f2), because the pin does not build on main (#23885 / #23908). check:console-sha matches. Playwright with /opt/pw-browsers/chromium, form sign-in as the seeded admin.

  1. POST /api/v1/packages {"manifest":{"id":"com.qa.recheck","name":"QA Recheck","version":"1.0.0","type":"app","namespace":"recheck"},"enableOnInstall":true} → 201.
  2. PUT /api/v1/meta/object/recheck_gate?package=com.qa.recheck: label "QA Gate", pluralLabel "QA Gates", fields name and kind → 200.
  3. PUT /api/v1/meta/view/recheck_gate.default?package=com.qa.recheck {"name":"recheck_gate.default","object":"recheck_gate","viewKind":"list","isDefault":true,"config":{"type":"grid","columns":["name","kind"]}}, with no label → 200.
  4. GET /api/v1/meta/view/recheck_gate.default/layers: overlay and effective carry no label.
  5. GET /api/v1/meta/view/recheck_gate.default: item.label: "recheck_gate.default". It is appended as the last key, after _diagnostics.
  6. Browser: /_console/apps/com.example.showcase/recheck_gate shows the header "QA Gates", and the only view tab reads recheck_gate.default. Same on two fresh loads.

The same fill reaches stock form views. GET /api/v1/meta/view lists showcase_task.form, showcase_task.tabbed, showcase_contact.create and others with label equal to their name.

  • Expected: a view with no authored label is served with no label (or null), as the container slots already are. The console then falls back to a human string (the object's plural label, or its "All records" wording).
  • Actual: the served document asserts a label nobody wrote, and it is the machine name.

Where

  • objectstack main 7277c07f:
    • packages/spec/src/system/i18n-resolver.ts:481 is const fallback = view.label ?? view.name; in resolveViewLabel.
    • :1186: translateView writes it back unconditionally (return { ...view, label, … }).
    • packages/rest/src/meta-item-read-gate.ts:2881: the single-item read runs it through translateMetadataDocument. :2823 does the same for the list read.
    • :3023 (if (key === 'label' && input.label == null && value === input.name) continue;) drops it for container slots only.
    • The resolver's own pin, packages/spec/src/system/i18n-resolver.test.ts:294-303, pins the resolver's return value, not the served document.
  • objectui main 023087f63 is the consumer half: packages/app-shell/src/views/ObjectView.tsx:956, const text = view.label || view.name || view.id;. Even with the server fixed, a label-less view would still fall back to the machine name here, so the console needs a human fallback too. That is a follow-up on objectstack-ai/objectui, filed when this contract lands.

Done when

  • The single-item and list meta reads serve a view with no authored label without an invented label, the same way the container path does. A REST pin asserts that GET /meta/view/NAME for a label-less stored view carries no label equal to name. Translated labels still resolve.
  • A consumer card on objectstack-ai/objectui is opened for viewTabLabel's fallback (human string, never the qualified name).

Reader who acts: the REST/i18n read-path owner (domain:services or domain:spec execution seat; triage picks). The diff is in packages/spec/src/system/i18n-resolver.ts translateView / resolveViewLabel or in the REST read gate's view translation.

Dedupe

Re-checked by a cloud QA session dispatched under #23878 (maintainer ruling there, 「派云卡片复核这 7 条,授权里全部立卡。有时间间隔就行」).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p1 · target:v18 · domain:cli · area:api · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T16:00Z

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

    • Why p1 + target:v18: a /meta read serves a label nobody authored. Dropping the invented value narrows the /meta door's served shape, which is the protocol-first class. Meanwhile the console shows recheck_gate.default as a view tab.
    • Why domain:cli: the read path is packages/rest (meta-item-read-gate.ts, whose view-container slots already drop the same fallback). The single-item and list reads take the same rule.
    • For the claimant: objectui's viewTabLabel must then fall back on its own when label is absent. If it does not, file the objectui half as its own card (the self-split rule).
  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    priority:p1High: required for production / M2
    and removed on Oct 16, 2026
  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (#23967: the /meta single-item and list reads serve a view with no authored label without the label the read-side translator invents from its name, by the rule the view-container path already applies, on both hosts through the one read gate) · 2026-10-16T17:08Z
    Session: session_018t6a5rvqPRvEDMCVVW9kYT
    Account: os-tesla (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23967-view-label-not-invented
    Worktree: objectstack-issue-23967
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    Scope: batch 3. Dev runs in flight before this act: #23914 and #23909. #23181 slice 4's dev run has ended; its PR #23994 is in review. This card takes the third slot. Verify-lock depth at this act: 1 (#23914's run holds it; the queue is empty).
    Order, read at this act: no open p0 or pm:blocking domain:cli card is in pm:queue. execution-duties.md ranks target: board items above p1. Of the two target:v18 bugs in the queue, #23909 went to this seat at 16:55Z. This card is the other. #23977 (p1, no target:) ranks after it.
    File surface (read on origin/main 2e1562091c; stop on a breach and explain it in the report):


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23967,
    "status": "done",
    "branch": "claude/issue-23967-view-label-not-invented",
    "pr": "#24013",
    "session": "session_018t6a5rvqPRvEDMCVVW9kYT — subagent run; the parent's harness-stamped id, carried on every commit's Claude-Session line and confirmed by the relay route (CLAUDE_CODE_REMOTE_SESSION_ID)",
    "premise_still_valid": true,
    "summary": "Reproduced on BASE adce192 on both hosts. GET /api/v1/meta/view/recheck_gate.default and GET /api/v1/meta/view served a view stored with no label as label: \"recheck_gate.default\", on the standalone os dev and on the dispatcher createHonoApp builds. The rule the container path already applied is now ONE module-private helper in packages/rest/src/meta-item-read-gate.ts, withoutInventedViewLabel(input, translated), and all three view sites call it: translateMetaDocument's single-view arm (item read, wrapped by translateMetaEnvelope), translateMetaList's per-element map (list read), and projectViewTranslation (container; its inline check is replaced with the same semantics). A label equal to the view's name where the stored view carried none is no longer served; authored labels (including one equal to the name) and catalog translations are. HEAD serves no label key for the label-less view on both hosts and both reads, in every locale. The changeset is BREAKING, minor, Clause-②: no (narrowing), adr-0087: not-required (no-migration-prescription). The objectui half (viewTabLabel) is left to the seat, as the card's Done-when says.",
    "tests": "New pin file packages/rest/src/meta-view-label-not-invented.test.ts, 39 rows: §1 standalone routes over a real SchemaRegistry and ObjectStackProtocolImplementation, one row per locale x view; §2 translateMetaEnvelope / translateMetaList, the dispatcher's functions; §3 container CONTROL. pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 --project local src/meta-view-label-not-invented.test.ts passed 39/39. REVERSE VERIFICATION, the whole file at BASE (on-disk blob 93e978309 == BASE blob): 15 red, exactly the label-less rows on both reads; all authored/translated rows and both §3 container rows green, so the container answer is unchanged. ABLATIONS via scripts/ablation-replace.mjs (anchor 1-to-0, blob changed; restore proven blob == HEAD blob 8fd8cdef6 and git diff HEAD empty): item site return translated gave 9 red, item-side label-less rows only; list site return out gave 9 red, list-side only; container site (helper bypassed in projectViewTranslation) gave the 2 §3 rows red. No rebuild between legs: the subject resolves to src by relative import (./meta-item-read-gate.js, and ./rest-server.js imports it relatively), so ablation-dist-preflight does not apply. Suites, post-merge at 6ff4da0 (the later commit 203a819 adds only a ledger JSON row), all under os-verify-lock with VERDICT command-exit 0: pnpm --filter @objectstack/rest typecheck exit 0; pnpm --filter @objectstack/rest test exit 0, 288 files, 5941 passed, 326 skipped; pnpm --filter @objectstack/rest test:repo exit 0, 5 files, 250 passed, 11 skipped; pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 --project local on the 25 local src/domains/meta-*.test.ts files exit 0, 513 passed; --project repo src/domains/meta-list-projection-parity.test.ts exit 0, 739 passed (runtime aliases @objectstack/rest to src). The same suites were also green pre-merge at 6c1755e. tsc -p packages/rest/tsconfig.test.json --noEmit --listFiles lists the new test with 0 error TS. LIVE BOOT from a scratch dir outside the repo (examples/app-todo compiled with os compile -o SCRATCH/objectstack.json, os dev --artifact ./objectstack.json --fresh, placeholder admin; recipe: POST /packages 201, PUT object 200, PUT view with no label 200, PUT view labelled 'All gates' 200), with no Accept-Language, en and zh-CN: BASE item 200 label 'recheck_gate.default', list element 200 label 'recheck_gate.default'; HEAD item 200 no label key, list element 200 no label key; authored 'All gates' on both; /layers overlay and effective carry no label on both. DISPATCHER PROBE (new HttpDispatcher(kernel) from the built runtime dist importing the built rest dist): BASE serves label = own name on item and list whenever a locale resolves (default locale or zh-CN); HEAD serves no label key; zh-CN catalog strings ('已标记', '全部闸门') served on both; with no default locale or no i18n service, label is absent on BASE and HEAD alike.",
    "gates": "LOCAL, at final HEAD 203a819: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 72 commands from merge base d132140, after a fresh fetch (the upstream commits since touch no derived input and no packages/rest file). All 72 were run with each exit captured before any pipe, and all 72 exited 0. Among them: check:adr-0087-registration (not-required (no-migration-prescription) accepted), check:changeset-no-major, check:empty-changeset, check:engine-double-contract (1017 pinned), check:test-source-alias, check:cross-package-test-inputs, check:nul-bytes, check:dual-build-cjs-loads (110 entry points across 68 packages load). node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE over the COMMAND :: exit CODE record printed 'Run reconciliation — 72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN' and '✓ … a DERIVED zero — all 72 recorded an exit code and none of them is 3'. An earlier union at 6ff4da0 had check:engine-double-contract exit 1 (RETAINED: the new pin's findOne double was unledgered), fixed by the gate's own --write (1 row added, 0 lost) in 203a819, and check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET), which measured on the final run. Lint: dispatch-gates names no lint family; pnpm exec eslint --no-inline-config packages/rest/src/meta-item-read-gate.ts packages/rest/src/meta-view-label-not-invented.test.ts exit 0. Local-only families check:dev-prereqs and check:skill-frame-freshness were not owed: no packages/spec build or frame file is touched. CI check-runs on 203a819, read once at about 18:41Z: success for Check PR Size, 'No other open PR may claim the same issue', 'No other open PR may claim the same single-writer path', 'Part-of PR must not also close its card' and 'The card this PR closes must claim this branch'; Packed-tarball smoke skipped; Auto Label queued; in_progress for Check Changeset, Lint · ESLint, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, the three Lint · scoped lanes, Type Check · workspace / source gates / consumer gates / debt ledger, Governed Surface Queue Guard, Check Documentation Links, 'Flag docs affected by code changes' and filter. Test Core and Build Core had not appeared yet. CI convergence is the PM's.",
    "mcp_calls": "0 — no MCP GitHub tool was called",
    "api_writes": "2 relay strokes as objectstack-fleet[bot], each one session-token POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml. (1) pr_create (run 38949076029): POST /repos/objectstack-ai/objectstack/pulls (draft forced) + POST /repos//issues/24013/assignees (os-tesla); the relay read back 10474 bytes stored identical, and a REST read confirmed draft:true, the body diff-identical and the session-URL footer once. (2) comment: POST /repos//issues/23967/comments (this report). label-write: none, as dispatched. git push x7 to the branch (not REST). Reads: single-card REST GETs of the issue, its comments, PR 24013 and its check-runs.",
    "deviations": [
    "File surface +1: scripts/engine-double-contract.pinned.json gains one row (packages/rest/src/meta-view-label-not-invented.test.ts, findOne, pinned 1). check:engine-double-contract REDs a new pinned engine double that the ledger does not record and prescribes --write; the regeneration printed 0 rows lost and moved no other row. Declared in the PR's 'Surface note'.",
    "Merged origin/main once before opening the PR (AGENTS.md §10), merge commit 6ff4da0, because #23990 had landed in packages/rest (rest-server.ts and ui-routes.ts; no overlap with this diff). Rebuilt the rest closure plus plugin-security and re-ran the suites post-merge.",
    "Live boot used examples/app-todo compiled into a scratch artifact rather than the card's showcase, so that it booted from a directory outside the repo. The card's object PUT now needs sharingModel (422 security-owd-unset without it).",
    "Commit trailers use AGENTS.md's model-free pair (Co-authored-by: Claude) instead of the harness reminder's model-named trailer, which AGENTS.md and the pre-push hook refuse. The PR footer uses AGENTS.md's session-URL form instead of the harness's suggested footer.",
    "label-write: zero writes, as dispatched. skip-changeset does not apply: the diff publishes @objectstack/rest, so it carries its changeset."
    ],
    "files_changed": [
    "packages/rest/src/meta-item-read-gate.ts",
    "packages/rest/src/meta-view-label-not-invented.test.ts",
    ".changeset/23967-view-label-not-invented.md",
    "scripts/engine-double-contract.pinned.json"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: none (not a filing class) · reach: none measured · PM assumption 5, other translatable types. The object, app, dashboard, dataset, page and picklist translators serve label only from the catalog or the document (conditional spread or ?? doc.label); none falls back to name. action does: resolveActionLabel answers action.name and translateAction always writes label. But ActionSchema declares label required (packages/spec/src/ui/action.zod.ts:959), so no schema-valid stored action reaches the fallback · carrier: none; noted in the PR's Acceptance notes, not filed · dedupe: resolveActionLabel action.name fallback, translateAction label, served action label invented",
    "class: none · reach: no served door after this PR · PM assumption 6, for domain:spec. resolveViewLabel's view.label ?? view.name (packages/spec/src/system/i18n-resolver.ts:481) is unchanged. translateView's callers in this repo: this module's three sites (all now drop the fallback) and the public-form route in packages/rest/src/rest-server.ts (about :9391), which takes only the translated config and keeps its own label reading. packages/cli/src/utils/i18n-extract.ts names both functions in docblocks only, with no import, so the dispatch's 'feeds … the CLI's i18n-extract.ts' is falsified. objectui main 63d00da calls neither (docblock mentions only) · carrier: domain:spec seat; noted, not filed · dedupe: resolveViewLabel fallback view.name, translateView invented label, exported resolver return type",
    "PM assumption 4, consumers; no conflict and nothing to file. GetMetaItemResponseSchema.item is z.unknown() and GetMetaItemsResponseSchema.items is z.array(z.unknown()). ViewItemSchema's label is optional. No packages/client type requires a served view's label, and no test in packages/** or examples asserts that a served view's label equals its name. The CLI's serve-view-container-read integration test asserts authored labels, and the dogfood view-container-cross-package-default test compares the item and list reads, which now take the same rule. objectui's viewTabLabel falls back to view.name · carrier: none",
    "carrier: the domain:cli seat, at landing, per the card's Done-when · the objectui half (ObjectView.tsx viewTabLabel, view.label || view.name || view.id, about :956 on objectui main 63d00da) needs a human fallback; until then the console tab keeps showing a machine name for a label-less view · noted, not filed by dev",
    "class: none (unestablished) · reach: scratch setup only · os dev --artifact from a directory with no node_modules logged runtime module load FAILED … Cannot find package '@objectstack/spec' imported from the artifact's runtime bundle, and app-todo's hook task_logic was refused at boot. It does not touch /meta. Whether an artifact is meant to boot without its dependencies installed is not established · carrier: none; noted in the PR, not filed · dedupe: artifact runtime bundle bare import @objectstack/spec, os dev --artifact outside project, runtime module load FAILED",
    "carrier: PM (container hygiene, not a repo finding) · /tmp/claude-0/-home-user/ab77ba73-22e7-5d29-a840-c87e921818a0/scratchpad/issue-23805 held 21G (files last written 06:45–08:49Z) and filled the shared disk to 99% at about 18:13Z. One tool output was lost to ENOSPC. By about 18:30Z the space had been freed by someone else (22G available); this run deleted nothing outside its own files"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPTed: PR #24013 at 203a8192f6 · domain:cli seat 2 · os-tesla · session session_018t6a5rvqPRvEDMCVVW9kYT · 2026-10-16T19:06Z · holder of claim 6220711918.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #24013 → aefab7c2c6 (#23967, Fixes) · domain:cli seat 2 · os-tesla · session session_018t6a5rvqPRvEDMCVVW9kYT · 2026-10-16T19:33Z

    Reading Result
    PR #24013 object merged: true, merge commit aefab7c2c6, merged 2026-10-16T19:33Z through the merge queue (enqueued 2026-10-16T19:11Z)
    origin/main aefab7c2c6 is an ancestor of origin/main: 4 files, +417 / −11, as reviewed at 203a8192f6. meta-item-read-gate.ts declares withoutInventedViewLabel (1 hit)
    queue branch no gh-readonly-queue/main/*pr-24013* head remains
    • Review chain: ## Contract review 6222526815 (CONTRACT_REVIEW_TIER, the (narrowing) face, PASS), ACCEPT 6222734061, card pointer 6222758377.
    • What landed: the /meta single-item and list reads serve a view stored with no label without the label the translator made from its name, on both hosts. Authored labels and catalog translations are served as before.
    • Still owed, by the Done-when: the objectui half. viewTabLabel (ObjectView.tsx about :956, view.label || view.name || view.id) still shows the machine name for a label-less view. This seat files it on objectstack-ai/objectui in its next filing window and links it here.
    • pm:dispatched is removed in the stroke after this comment.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    The objectui half is filed: objectstack-ai/objectui#12774 · domain:cli seat 2 · os-tesla · session session_018t6a5rvqPRvEDMCVVW9kYT · 2026-10-16T19:42Z

    viewTabLabel (packages/app-shell/src/views/ObjectView.tsx :1013 on objectui 360e27a) falls back to view.name, so a label-less view's tab still shows its qualified name. PR #24013 removed the invented label from the server, which leaves the console exactly as it was until that card lands. This closes the second line of this card's Done-when.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:clipriority:p1High: required for production / M2target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions