Skip to content

Internal /meta spelling corrections in objectstack: SDK item-route typing normalizes toward singular; metadata-api.mdx stops teaching the plural as a co-equal form (step ③ residue of #9180) #10078

Description

@os-zhuang

Step ③ residue of the #9180 ruling, objectstack half, carved by the spec seat under the maintainer's direct reassignment (2026-08-19, verbatim: 「改派给 spec 席,现在就拆」; audit comment #9180 comment 5349687344). Sibling cards filed at destination in objectui and cloud.

The ruling being implemented (⛔ not re-litigable)

Maintainer re-weigh, 2026-08-17 (#9180 comment 5311434183): step ③ is re-scoped to internal spelling correction only, across the three self-owned repos — fix every incorrect plural spelling; the external break is deferred. Verbatim, untranslated: 「② 照原样做;只需要修正 objectstack objectui cloud 中错误的写法。」 The direction ("singular is the only canonical spelling") stands; internally the repos become 100% singular.

Measured sites (2026-08-19, origin/main — re-verify at your branch point)

  1. Client SDK variable-typed item routespackages/client/src/index.ts (getItem / saveItem / deleteItem / getHistory family): type is string with no type-level guard, so a caller-supplied plural flows straight into the URL. Tighten typing / normalize toward singular per the ruling. (The getLegalNextStates string itself moves in the step-② card — Retire the plural /meta/objects/:name/state/:field registration: SDK moves to the singular twin, the collision's registration-order pin is deleted (step ② of the #9180 ruling) #10077 — not here; coordinate if both are in flight.)
  2. content/docs/api/metadata-api.mdx:31-36 — currently teaches the plural as co-equal: "Metadata type name, singular or plural — object and objects address the same type" / "both forms exist". Rewrite to teach singular as the canonical spelling; the fold remains an undocumented compatibility tolerance for external callers, not a documented promise.

Constraints that ride verbatim

  • Internal correction only — ⛔ no accept/reject change anywhere. The boundary fold (META_URL_TO_SINGULAR) stays; no new refusals (re-weigh item 3). If any slice of your diff turns out to change what a request is answered with (not merely what we emit or teach), STOP and report the fork — that slice belongs at the claude-fable-5 floor and is not this card.
  • Tier per the re-weigh: consumer-sweep cards default opus unless a slice touches accept/reject behaviour, in which case the fable floor re-applies.
  • content/docs/releases/** is read-only (release-owned) — release pages naming the plural stay untouched.

Routing note: stamped domain:cli under the direct-dispatch channel that authorized this carve (landing packages are client + the api docs of the cli-owned REST face); triage may re-split the docs half to devx on first touch.

Dedup checked 2026-08-19: zero existing cards for either site.

Part of #9180.


✅ Site 2 is DONE — PR #10337, merged 2026-08-20 (squash d0082236d)

content/docs/api/metadata-api.mdx now teaches the singular as the spelling. Verified on origin/main by content, not by the PR flag: line 31 reads "Metadata type name — the singular spelling (object, view, flow)".

That PR was deliberately written as "Part of", never "Fixes", so it could not close this card while site 1 remains open. closed_by_pull_requests: 0 at merge time confirms it did not.


Block state

⚠️Maintained by the domain:cli seat (session session_019bmVFqoQPq63zhKrxdYG1r). Everything above the first --- is the filing seat's text, unmodified. This section carries the machine-readable dependency for the pm:blocked I applied — it belongs in the body because the H4 predicate matches /^[ \t]*Blocked-by:/ against issue.body only.

Blocked-by: #10340

Superseded block: the previous Blocked-by: #10077 (hard file serial on packages/client/src/index.ts) is satisfied#10077 merged, and PR #10337 confirmed getLegalNextStates already spells the segment object on today's main. That block is retired, not carried.

The new block, and why site 1 is dead as written. Site 1 asks the client SDK to normalize the emitted :type segment toward singular. That is now measured to be unsafe as specified — it would change what a request is answered with, which this card's own constraints forbid at this tier. Executed against the built @objectstack/metadata-core and @objectstack/spec/meta-spelling (PR #10337's body carries the full table):

spelling in URL map protocol fold REST org READ REST org WRITE
translation false translation org_1 org_1
translations true translation undefined undefined
email_template false email_template org_1 org_1
email_templates true email_template undefined undefined

The REST /meta doors decide organization scope from the raw URL spelling (organizationIdForMetaRead / organizationIdForMetaWrite, folding through the manifest map), while the protocol boundary folds through the complete URL map. For translations and email_templates the two maps disagree. So an SDK that folded translationstranslation before emitting would move an org-active caller from the env-wide row to the org-scoped row on reads, and would land a saveItem on an org-scoped row where it lands env-wide today. That is the accept/serve half of the boundary, not the emit half — this card's own STOP condition, hit exactly.

No partial fold either: folding only the spellings that happen to agree would fossilize the server's disagreement into the client — a second dialect, which Prime Directive #12 forbids.

#10340 is that map disagreement, filed as a defect in its own right. Until it is decided, there is no correct fold for site 1 to emit. Once it lands, re-derive site 1 against the resolved maps — and note the remaining "tighten the typing" route needs META_URL_TO_SINGULAR to expose literal key types, a packages/spec change outside this card's declared surface, so a re-scope will be needed then too.

Unlock-action: when #10340 closes, re-check on the merged ref whether the two maps now agree. Agreed ⇒ site 1 becomes writable; re-scope the file surface to include packages/spec and return to pm:queue. Not agreed, or resolved by a different route ⇒ re-read before assuming this card survives at all.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions