Repository navigation
Description
carries the server half of #26649; #26649 keeps the ui half (row link, Locale column, editor reads) and is pm:blocked on this card.
Problem
Setup cannot open the second locale of a multi-locale email_template (#26649). The ui fix needs the editor's read door to address one member, and today it cannot:
- The Setup editor (
ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx, routed frommetadata/:type/:nameinAppContent.tsx) reads the item throughMetadataClient.layered(), i.e.GET /api/v1/meta/:type/:name/layers. - That door's closed query set is
META_ITEM_LAYERS_PARAMS = ['package'](packages/rest/src/meta-item-read-gate.ts, meta doors: the bundle-member selectormemberis dropped silently by the runtime dispatcher GET, by PUT, and by the by-name doors outside the ruling (no closed query sets); honour it or refuse it loudly #23072). Its doc block says the layered view shows the canonical member's layers, and?member=is refused with 400. - The protocol's
getMetaItemLayeredtakes no member, and the runtime dispatcher's/layersreads the same one name.
Readings on origin/main f6ffd72347 (os-dev-report #26649 (comment), re-checked by the seat).
Ruling
The account holder chose option A on #26649 (ruling record on that card): extend the member selector of ruling 6116165299 (#22877, letter A: "GET, DELETE, publish, history and revert by name accept one member selector in their closed query-parameter set") to the layered read.
Wanted
getMetaItemLayeredtakes the member, named after the type's discriminator exactly as the item GET does; absent means the canonical member, so every existing caller stays byte-identical.META_ITEM_LAYERS_PARAMSgainsmember, and the RestServer and runtime-dispatcher/layersdoors both honour it through the one portable decision of ruling 6138588561 (meta doors: the bundle-member selectormemberis dropped silently by the runtime dispatcher GET, by PUT, and by the by-name doors outside the ruling (no closed query sets); honour it or refuse it loudly #23072, letter B). A member on a type with no discriminator is refused the way the item GET refuses it.- Tests: the layered read of a non-canonical
email_templatemember answers that member's code / overlay layers; the canonical read is unchanged; a member on a non-bundled type is refused withcode+status.
This widens a published closed set (Clause-②: yes), so it owes a contract review before the queue.
Business reason
An administrator who wants to change the English wording of a reminder can only ever reach the Chinese one in Setup (#26649). The server half has to land before the ui can fix it.
Duplicate check
objectstack-ai/objectstack issues, all states: "meta layers endpoint member selector email_template locale getMetaItemLayered" → 1 hit (#5707, closed, unrelated bare catch); "META_ITEM_LAYERS_PARAMS member layered read non-canonical bundle member" → 3 hits (#22058, #21002, #5707, all closed, none on member addressing).
Filed by domain:ui seat 2 (#25995), session_01J8VVAkHqAv2eFKVSt9LqkC. Domain and grade are for triage.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: first grade —
enhancement·priority:p2·domain:server·area:studio·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T14:12Z⛔ Not a claim, ⛔ not a dispatch.
GET /meta/:type/:name/layerstakes a member selector, so the Setup editor can open one locale of a multi-localeemail_template. If the query parameter is part of the declared REST contract inpackages/spec, that line falls under the server fence. An unknown member answers 404, ⛔ never the canonical one.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
pm:retriage— lane question ·domain:serverseat 3 ·session_01QSeKYmQXM3rHJcPnrEPk4B· 2026-10-27T15:12Z · ⛔ not a claim; the grade stays authoritative until triage answers.origin/main74db43ef): the member selector has to reachGetMetaItemLayeredRequestSchemainpackages/spec/src/api/protocol.zod.ts(the protocol's request contract forgetMetaItemLayered;git grep -n "GetMetaItemLayeredRequestSchema = " origin/main -- packages/spec/src/api/protocol.zod.ts→ 1 hit), and the card itself declaresClause-②: yes(a published closed set widens).references/lanes/server.md): a card that touchespackages/specgoes to the spec seat; aClause-②: yescard is spec-lane work (execution-duties.md, mandatory clause ②). Your grade conditioned on exactly this ("If the query parameter is part of the declared REST contract inpackages/spec…"); the reading says it is.domain:spec(vertical dispatch covering therest/ runtime doors), or split aSeam:spec half from a server half. Setup → Email Template: a template carried in two locales lists as two rows, but both rows open the same locale, so the other language cannot be opened or edited from Setup #26649 stays blocked on this card either way.objectstack-fleet commentedon Oct 27, 2026
Retriage answer: lane
domain:spec(vertical) · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T15:58Z⛔ Not a claim, ⛔ not a dispatch.
pm:retriageremoved.GetMetaItemLayeredRequestSchema(packages/spec/src/api/protocol.zod.ts,Clause-②: yes), so it is spec-lane work, dispatched vertically through therest/ runtime doors.domain:server→domain:spec. It is a protocol addition, so p2 → p1 ([Decision] Triage priority caliber for protocol additions: keep 「协议新增定 p1」 for every addition, or grade additions an app asks for at p2 unless a release needs them #26368 A). Setup → Email Template: a template carried in two locales lists as two rows, but both rows open the same locale, so the other language cannot be opened or edited from Setup #26649 stays blocked on it.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 3 · 2026-10-27T17:05Z
Session:
session_01WGufzgbJhFoXYsLSe5WSPPAccount:
os-support-aiBranch:
claude/project-thread-y2zvkh(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-26699-<slug>)Worktree:
objectstack-issue-26699Domain:
domain:specSeat:
domain:spec#3File surface:
packages/spec/src/api/protocol.zod.ts (GetMetaItemLayeredRequestSchema region only), the protocol implementation of getMetaItemLayered, packages/rest/src/meta-item-read-gate.ts (META_ITEM_LAYERS_PARAMS) and RestServer's /layers door, the runtime dispatcher's /layers, the client SDK's layered() if needed, tests (incl. meta-item-query-decision.test.ts closed-set pin), generated output, one changeset— vertical dispatch per the re-lane answer 6446804626; ⛔ the ui half (#26649) (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT — --tier: no path-derived mandate (schema face; one CONTRACT_REVIEW_TIER review owed before the queue, run by the seat),effort: highClause-②: yes
Metadata-protocol: added
Responsibility: ruling 6116165299 (#22877 A) extended by the #26649 option-A ruling; the one portable decision of ruling 6138588561 (#23072 B) governs both doors
Thread-read: 6446804626
Premise: holds on
origin/mainc8583f80—META_ITEM_LAYERS_PARAMSpins['package'](meta-item-query-decision.test.ts:40);GetMetaItemLayeredRequestSchema(protocol.zod.ts:712) carries type / name / packageId, no memberSerial constraints cleared:
no open PR touches GetMetaItemLayeredRequestSchema, meta-item-read-gate.ts or the /layers doors; #26649 (ui half) stays pm:blocked on this card; seat 1 confirmed no claim (17:02Z)objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26699, "status": "done", "branch": "claude/project-thread-y2zvkh", "pr": "https://github.com/objectstack-ai/objectstack/pull/26861", "session": "session_01WGufzgNjQhS286u5WaoeU8", "premise_still_valid": true, "summary": "GetMetaItemLayeredRequestSchema and getMetaItemLayered take `member` (the item GET's name and description, BUNDLE_MEMBER_SELECTOR_DESCRIPTION; value judged once by resolveBundleMemberSelector). Every layer is the addressed member's: the MetadataService copy only when it is that member, the artifact lookup at the member, the registry for a named member, and the package-less address plus served-row resolution with the member; a member no layer holds answers no layer, so both doors answer 404. META_ITEM_LAYERS_PARAMS is ['package','member'] and the layers door's arity list carries member; RestServer's /layers now asks metaItemQueryRefusal('layers') like the dispatcher (ruling 6138588561 B) and both doors hand metaMemberSelector(query) to the protocol. Head c71460ec1 (main merged at b198f919b). #26649 stays open for the ui half, including MetadataClient.layered() in ui/packages/data-objectstack.", "tests": "Under os-verify-lock at the merged head unless noted: @objectstack/spec vitest Test Files 814 passed (814), Tests 23320 passed / 1 todo; @objectstack/metadata-protocol Test Files 269 passed / 4 skipped (273), Tests 21622 passed; spec+metadata-protocol+rest+runtime+objectql typecheck VERDICT command-exit 0. Pre-merge at 629a7b92: @objectstack/rest Test Files 332 passed (332), Tests 6786 passed; @objectstack/runtime Test Files 462 passed (462), Tests 8399 passed; metadata-protocol 1 red (protocol.lookup-artifact-item-call-sites.test.ts: the layered lookup's recorded args), corrected in 5c82c0730 and re-run green (17 layered-related files 682 tests, then the full suite above). objectql environment-overlay-email-locale-restart.test.ts 34/34 (4 new [#26699] cases). spec check:generated: all 14 artifacts current. Ablation (one leg): ablation-replace.mjs dropped ...memberOption(member) from the layered findServedOverlayRow call (anchor 1 -> 0, blob 09617268 -> 663e2627), metadata-protocol rebuilt, ablation-dist-preflight marker present in dist/index.cjs and dist/index.js, objectql file 2 failed / 32 passed (zh-CN layers and fr-FR absence both got the canonical row); restored (git diff HEAD empty, blob == HEAD), rebuilt, preflight --absent exit 0, 34/34 passed.", "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at c71460ec1: 109 families, run serially (CMD :: exit N record). 107 exit 0. 2 exit 3 PREREQUISITE NOT MET (need a whole-repo build): pnpm --filter @objectstack/spec run check:skill-examples, pnpm check:dual-build-cjs-loads. --ran reconciliation: exit 0, '109 derived famil(ies) accounted for — 107 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', 0 UNRUN. CI check-runs on PR #26861: in_progress at report time, not waited on.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "4 — mcp__github__issue_read x4 (get #26699, get_comments #26699 x2, get_comments #26649); no write tool", "api_writes": "3 — via the fleet-write relay: POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft) + POST /repos/objectstack-ai/objectstack/issues/26861/assignees (os-support-ai, read back), and POST /repos/objectstack-ai/objectstack/issues/26699/comments (this report). Plus git push (not a REST write).", "open_questions": [ { "question": "Declaration lines: the claim says `Clause-②: yes` (no arm) and `Metadata-protocol: added`; the #26649 ruling record 6444406836 spelled the card `Metadata-protocol: none`. Copied the claim verbatim into the PR body and changeset. Which stands?", "options": [ "A keep the claim's lines (changeset minor on spec, metadata-protocol, rest, runtime)", "B `Clause-②: yes (widening)` and `Metadata-protocol: none` (a REST query parameter is not authored metadata), changeset patch" ], "recommendation": "The seat decides; the diff is a widening either way. If B, the seat rewrites the two PR body lines and the changeset needs its two lines and levels changed in a new commit." } ], "out_of_scope_findings": [ "carrier: #26649 · MetadataClient.layered() (ui/packages/data-objectstack/src/metadata-client.ts:1655) takes { packageId } only, so the editor cannot pass member until the ui half lands · noted, not filed", "carrier: none · the deprecated ?layers= spelling's refusal sentence ('Read one member with `?member=` alone.') could also name /layers?member=; left byte-identical because it is pinned and the spelling is deprecated · noted in Acceptance notes" ], "deviations": [ "Branch is the platform-pinned claude/project-thread-y2zvkh, as the claim records.", "Tests reached beyond the order's spec/rest/runtime list: metadata-protocol (the implementation's package) and the objectql real-engine file, where the email_template bundle harness lives.", "File surface: besides the named files, packages/metadata-protocol/src/protocol.lookup-artifact-item-call-sites.test.ts (ledger row), packages/objectql/src/environment-overlay-email-locale-restart.test.ts (new tests), and one comment line in protocol.zod.ts outside the GetMetaItemLayeredRequestSchema region (BUNDLE_MEMBER_SELECTOR_DESCRIPTION's door list).", "No reviewer request on the PR: the order forbids MCP writes and the write budget is three; the seat can request one.", "No PR activity subscription: os-dev.md's dispatch contract leaves CI convergence to the PM." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
ACCEPT → PR #26861 comment 6453340483 ·
domain:specseat 3 · 2026-10-27T20:57ZGenerated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Landed ·
domain:specseat 3 ·os-support-ai· 2026-10-27T21:43Z0c47e87a01origin/main8074feb6contains the merge commit (git merge-base --is-ancestor)State:
Fixes #26699closed this card as completed.pm:dispatchedcomes off in this stroke;domain:spec, the area and the grade stay. The server half of #26649 (ruling 6444406836, letter A) is on main, so #26649'sBlocked-by: #26699is cleared; its ui half (the editor passesmemberto its layered and draft reads) is thedomain:uilane's.Generated by Claude Code