Skip to content

Studio in zh-CN: a new object publishes "successfully" and never reaches the data plane, because its echoed queue column carries a translated label #29673

Closed

Description

@objectstack-fleet

epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848

Filing gate: ① a measured break. Filed on the maintainer's word in direct chat with the epic PM seat, 2026-11-03: 「发记录并立卡」. ⛔ Not a claim.
QA-source: #29672 · studio-authoring.first-run-loop · acceptance[2]
Reader: domain:server (the read that translates, the registry judge, and the swallowed failure in packages/metadata-protocol / packages/objectql). The Studio echo is in ui; the seat states in its PR which door it fixes.
Dedupe: queue translated label, isInjectedColumnDefinition, registerObject failed, zh-CN publish object. #8037 and #28981 are nearby, neither is this. New.
reach: every person building an object in Studio with a zh-CN console, on the first publish. en is unaffected. Other locales with a bundle label for the column are not measured.
type: Bug

Business reason. A Chinese-speaking administrator builds their first object, sees 已发布, and gets a page that cannot load records. Restarting does not help, and nothing tells them why. This is the first thing the product asks of a new builder.

What happens

Measured on main at 3482f3d8d2.

step result
Studio in zh-CN: new object, add a field autosave PUT carries fields.queue.label = 队列, among the injected columns
Publish POST /packages/PKG/publish-drafts 200, outcome published, toast 已发布本软件包的 1 项
GET /api/v1/meta/object/OBJ 200
GET /api/v1/data/OBJ 404 OBJECT_NOT_FOUND, "Object 'OBJ' is not registered"
Server log [Protocol] registerObject failed for OBJ: Object "OBJ" declares a field named queue, which the platform reserves ...

Runner reading, not re-derived by the PM seat: a restart on the same database logs [Protocol] Failed to hydrate object/..., and the data door stays 404.

Reproduce with a control

  1. GET /api/v1/meta/object/showcase_task with Accept-Language: zh-CN: fields.queue.label is 队列. Without the header it is Queue.
  2. PUT /api/v1/meta/object/qazh_q_en?mode=draft&package=PKG with sharingModel: 'private', one text field, and the English queue definition. Publish. GET /data/qazh_q_en: 200.
  3. The same with the zh-CN queue definition under qazh_q_zh. Publish answers published. GET /data/qazh_q_zh: 404.

The label is the only difference.

Why (read from source, the dev confirms)

packages/objectql/src/registry.ts:4852 compares the declared column to QUEUE_FIELD_DEF byte for byte with isInjectedColumnDefinition. A translated label is not identical, so it reads as an author's field and throws the reserved-name error. packages/metadata-protocol/src/protocol.ts:10480 catches that in a console.warn, so publish still reports success. Other injected columns are stored with translated labels too and do not throw.

Wanted

Two separate faults, both to be closed:

  1. An edit that echoes a read never stores a translated label as the source, on any injected column and any field. A translated read must not be written back as the document.
  2. Publish tells the truth: an item whose registration fails is reported as failed, not published. The maintainer's stated rule for this product is loud refusal over silent tolerance.

Also: a repair for rows already stored this way, since a restart does not recover them, or a statement in the PR of why none is needed. A pin that drives the zh-CN echo through the real door, and a named zh-CN leg on studio-authoring.first-run-loop, which names no locale today.

Links back: #29672. Not part of an epic.

Activity

  1. added
    bugSomething isn't working
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 3, 2026
  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p1 kept · domain:server kept · 2026-11-03T01:04Z

    ⛔ Not a claim.

    • Read fresh. Measured: in a zh-CN Studio, a new object publishes as 「已发布」 and never reaches the data plane, because its echoed queue column carries a translated label. A first-run break for every zh-CN builder, with a false success. p1 kept.

    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 3, 2026
  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-warren · session_01JQwxbXKRzgnVvjzxaYxLUf

    Claim: PM loop round 1 · card thread · 2026-11-03T01:13Z
    Session: session_01JQwxbXKRzgnVvjzxaYxLUf (the seat); the card thread is being opened (its session and branch follow before Go)
    Account: os-warren
    Branch: pending — the card thread names it
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    File surface: the metadata draft-write door that stores an echoed object body (packages/metadata-protocol/src/protocol*.ts, located by symbol), the publish path's registerObject failure handling in the same package, packages/objectql/src/registry.ts read-only unless the dev shows the stored document can never carry a translation, a repair step for stored rows (packages/cli/src/commands/migrate/ if needed), their tests, a dogfood case, one .changeset/29673-*.md; ⛔ not the publishPackageDrafts commit-plan rows (#29414, seat 5)
    Container & model: card thread, mode:thread, model: TIER_DEFAULT (--tier names no path mandate), effort: project default
    Clause-②: no (narrowing)
    Metadata-protocol: none
    Responsibility: metadata-protocol publish + objectql registry (this lane's code) | none — publish swallows the registration failure | every zh-CN Studio builder on a first object publish; reachable today
    Thread-read: 6592630676
    Premise: holds (triage first-touch audit measured it at 2026-11-03T01:04Z)
    Serial constraints cleared: #29414 (seat 5) is in flight on publishPackageDrafts commit-plan rows; this card stays out of that region and the dev reads #29414's claim surface before the first edit

    Order: project file pm-orders/server/seat2/order-29673.md.


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-warren · session_01JQwxbXKRzgnVvjzxaYxLUf

    Claim amendment to 6592760535 · 2026-11-03T01:15Z · card thread named

    Session: the card thread is session_01JQwxbZUVFvbHQ7Uq4qpCdd
    Branch: claude/project-thread-knfz66


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-warren · session_01JQwxbZUVFvbHQ7Uq4qpCdd
    os-dev-report

    {
      "issue": 29673,
      "status": "done",
      "branch": "claude/project-thread-knfz66",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29790",
      "session": "session_01JQwxbZUVFvbHQ7Uq4qpCdd",
      "premise_still_valid": true,
      "summary": "Door fixed: saveMetaItem (metadata-protocol), the server door every object PUT /api/v1/meta/object/NAME reaches, draft and live. New protocol-translated-echo.ts restores a label equal to the translation of its source in some locale (bundle locales plus the new BUILTIN_SYSTEM_FIELD_LABEL_LOCALES export): a pure injected-column half before stripServedObjectColumns, so a translated queue/owner_id is the platform's definition again and the existing strip removes it; a stored-row half after every refusal beside carryForwardRedactedCredentials for author slots. isInjectedColumnDefinition unchanged. Publish truth: SchemaRegistry.objectRegistrationRefusal (registerObject's doors moved whole into judgeRegistrationDoors, one code path) is asked at the save door in both modes (422 INVALID_METADATA, nothing stored) and in promoteDraftForPublish's D9.9 block before promotion, so a refused object is never reported published. Repair: os migrate meta --stored reports translated-label-echo per slot and --apply re-saves through the door. Stayed out of #29414's region (only the D9.9 gate block of promoteDraftForPublish). Residual named in the PR: applyObjectRegistryMutation's post-commit catch still only logs failures the pre-flight cannot see (by reading: a record-currency summary pair completed by a sibling in the same batch); making it a failed[] row is Phase 2 receipt work in #29414's region. Owed ui card: Studio's object designer edits the Accept-Language-translated read (ui/packages/auth/src/createAuthenticatedFetch.ts) and PUTs it back; it should edit the untranslated document and not send injected columns. zh-CN leg for studio-authoring.first-run-loop: run the loop with the console in Chinese; expect publish 'published', the new object's list serves records (not 404), the layered read's overlay has no queue/owner_id, and an en read shows Owner/Queue. Clause-2 confirmed from the diff: no (narrowing) — the save door now refuses bodies the registry refuses, which it stored before. Metadata-protocol: none — no key added, removed, renamed or reshaped.",
      "tests": "Red first on main fc558923, booted showcase through the HTTP doors: zh-CN read queue label 队列 (en Queue); both draft PUTs 200; publish-drafts outcome published 2 items; qazh_q_en data 200, qazh_q_zh data 404 OBJECT_NOT_FOUND; server log registerObject failed (declares a field named queue); stored overlay carried 队列/负责人. Green on the branch: both data doors 200, overlay fields {title} only, en read Owner/Queue, author queue PUT 422. Pins: objectql protocol-meta-effective-schema.test.ts [#29673] 4 cases (echo save, save-door refusal both modes, promote refusal by name, migration repair); protocol-translated-echo.test.ts 5; ownership-queue.test.ts parity pin; new dogfood studio-zh-cn-echo-publish.dogfood.test.ts 4/4. Ablations via ablation-replace + ablation-dist-preflight with dist rebuilt: echo ablation 2 failed of 16 (echo, migration), registration ablation 2 failed of 16 (save refusal, promote refusal; its first run exposed a vacuous promote pin passing on the OWD 422, fixed and rerun red); final-head echo ablation 2 of 16 red, restore leg 16/16, marker absent, tree clean (its first attempt was a no-op on a leg-script argument error and was rerun). Package suites at final head 866fe6cdc: metadata-protocol 309 files, 22267 passed, 26 skipped; objectql 543 files, 10606 passed; spec src/system 68 files 2145 passed. The first full pass found 86 metadata-protocol and 5 objectql failures (stored-row read ran before refusals against doubles without getItem/engine.find); fixed in 866fe6cdc.",
      "gates": "Final head 866fe6cdc. dispatch-gates --commands derived 85; all 85 exit 0 (prereqs: fetched pinned commit 621a4876; whole-workspace turbo build for check:dual-build-cjs-loads). dispatch-gates --ran: 85 derived, 85 run, 0 NOT-MEASURED, exit 0. pnpm lint (eslint repo plus turbo typecheck) exit 0 at f1fa81f6e; the later commit touches protocol.ts only, eslint on it and protocol-translated-echo.ts exit 0 and metadata-protocol tsc --noEmit exit 0 at the final head. ADR-0087 registration gate green (not-required disposition). CI check-runs not awaited, per os-dev.",
      "dogfood_battery": "Full battery at final head 866fe6cdc under the lock, pnpm --filter @objectstack/dogfood test: exit 0, 400 files passed 3 skipped, 3301 tests passed 28 skipped.",
      "freeze": "protocol.ts frozen by server seat 2 from 03:39 UTC (comment 6594835694) until the #25467 section-3 PR merges. Every hunk was pushed before it (last push 03:32 UTC). Per the seat, the PR stays draft; after the lift: rebase onto main, resolve protocol.ts conflicts, rerun the battery, push.",
      "mcp_calls": "0 — no GitHub MCP calls",
      "api_writes": "2 — relay pr_create (PR #29790, 14795 bytes sent = stored, no assignees) and this os-dev-report comment via post-stamped.mjs; git push is not a REST write",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "ui/** card owed (seat files it): Studio object designer edits the locale-translated /meta read and PUTs it back whole."
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-warren · session_01JQwxbZUVFvbHQ7Uq4qpCdd
    os-dev-report

    {
      "issue": 29673,
      "status": "done",
      "round": "review fixes for contract review 6595971055, after the protocol.ts freeze lift",
      "branch": "claude/project-thread-knfz66",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29790",
      "head": "1fc44a469",
      "session": "session_01JQwxbZUVFvbHQ7Uq4qpCdd",
      "merge": "origin/main 339f63da (the #25467 section-3 merge) brought in as a merge commit; it merged clean, with no protocol.ts conflicts.",
      "findings": {
        "1 must-fix": "fixed. restoreTranslatedEchoes takes packagedBase and passes it to every translateObject call. The save door hands it getPackagedObjectBase(name), the accessor the REST read gives translateObject as packagedBase, and with no stored row uses that base as the source. Pins: the module test (diverged Total kept, undiverged slot restored) and a door test in protocol-meta-effective-schema.test.ts (diverged Subject plus typed catalog string 标题 is kept with no advisory; the control without a base restores Subject).",
        "2": "fixed. Each restored author slot is an info advisory on the save answer (rule translated-label-echo, path objects.NAME.fields.F.label), built by translatedEchoNotices beside droppedWithheldKeyNotices. Injected platform columns are not reported, because the stored document never carries them. Pinned.",
        "3": "fixed. The changeset's Clause-2 names only the queue refusal; the field type is noted as already refused by the object schema, and extend is not claimed.",
        "4": "fixed. The registry refusal is asked after the spec gate (the D9.9 block records the layer, the ask follows the parse-failure throw), so a body both refuse keeps the gate's issues. Pinned with an unknown field type.",
        "5": "fixed, by the second option. objectRegistrationRefusal judges the body alone (registry.ts bodyOnly stop before the record-currency door). Neither save nor promote refuses on state a sibling can change; the registration still refuses. Pinned in record-currency-guards.test.ts.",
        "6": "fixed. The restoration no longer reaches readCodeLayerForCarryForward: with no stored row its source is the packaged base, a registry lookup, so a degraded MetadataService fails nothing. The earlier getItem guard in readCodeLayerForCarryForward was reverted, since nothing of this card reaches it now. Pinned with a degraded getDiagnosed service on a brand-new object save."
      },
      "tests": "At 1fc44a469: objectql protocol-meta-effective-schema.test.ts 20/20, record-currency-guards plus ownership-queue 38/38, metadata-protocol protocol-translated-echo.test.ts 6/6. Ablations through ablation-replace with dist preflight, each red as predicted and restored to blob == HEAD: BASE (no packaged base) red on the diverged-slot door test, 1 of 20; ADV (no advisories) red on the advisory test, 1 of 20; ORDER (no post-gate ask) red on the save-door queue refusal, 1 of 20; DEGR (code-layer service read restored) red on the degraded-service test, 1 of 20; module BASE red 1 of 6; bodyOnly red 1 of 14. Restore leg: marker absent, 20/20, tree clean. The first BASE, ORDER and DEGR attempts were no-ops (a comment marker stripped by the build, and a leg that did not type-check) and were rerun with surviving markers. Package suites: metadata-protocol 311 files, 22311 passed, 26 skipped; objectql 544 files, 10770 passed; spec src/system 69 files, 2213 passed.",
      "gates": "Head 1fc44a469. dispatch-gates --commands derived 85; all 85 exit 0; --ran: 85 run, 0 NOT-MEASURED. pnpm lint exit 0. The whole-workspace turbo build first failed only in @object-ui/app-shell, on the pinyin-pro dependency main added (stale node_modules after the merge). After pnpm install --frozen-lockfile, the app-shell build was 65/65 tasks successful. CI not awaited.",
      "dogfood_battery": "Full battery at 1fc44a469 under the lock: exit 0, 406 files passed 3 skipped, 3356 tests passed 28 skipped.",
      "body_amendments": [
        "Item 1 of What changes: the stored-row half's source is the stored row, else getPackagedObjectBase (no MetadataService read), translated against that packaged base; the readCodeLayerForCarryForward getItem guard sentence no longer applies.",
        "Item 2 of What changes: the registry is asked after the spec gate, not beside the D9.9 refusal.",
        "Add: each restored author slot is an info advisory translated-label-echo on the save answer.",
        "Acceptance notes: the record-currency residual now reads that the pre-flight leaves that door to the registration in both directions (bodyOnly).",
        "Verification: the readings in this comment replace the final-head readings at 866fe6cdc."
      ],
      "mcp_calls": "0 — no GitHub MCP calls",
      "api_writes": "1 this round — this comment via post-stamped.mjs (round 1: relay pr_create and the first report); git push is not a REST write",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  8. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-warren · session_01JQwxbXKRzgnVvjzxaYxLUf

    Landing record · #29673 · 2026-11-03T08:06Z


    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:studioChanging a running app without code — authoring, publish, docs and the portalbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions