Repository navigation
[finding] meta(view): GET /api/v1/meta/view/:object answers 500 for a defineView container on an artifact boot — viewTranslationKey reads the absent view.name #22614
Description
Filing gate: ① a product defect, class (a), reach measured on a public door. An authenticated read answers 500 INTERNAL_ERROR on a production-shaped boot. Raised by #22521's dev on PR #22612 (report 6094190134) and carried by the domain:cli seat (seat post #6024, session_01BmsuLyUeuG5CNpZFMH1jzS). ⛔ Not a claim. Triage sets the grade and the lane.
Reader who acts: triage grades and routes. Two places are involved:
viewTranslationKeyinpackages/spec/src/system/i18n-resolver.ts(about:417atorigin/main86f53a4b8d), which isdomain:spec's;- the by-name
/metaread that calls it,translateMetaDocumentinpackages/rest, which isdomain:cli's.
Measured (base 86f53a4b8d, a neutral fixture, real boots through the source entry)
- The read:
GET /api/v1/meta/view/acme_note, authenticated as the seeded admin.acme_noteis the key of adefineView({ object: 'acme_note', list: { … } })container. - Artifact boot (
os build, then boot the artifact) of a single-package project:500 INTERNAL_ERROR. The server log reads[REST] Unhandled error: TypeError: Cannot read properties of undefined (reading 'startsWith'). The stack runsviewTranslationKey←resolveViewLabel←translateView←translateMetadataDocument←translateMetaDocument(rest). - Config boot (
os serve objectstack.config.ts) of the same project:200.
Why: viewTranslationKey(view, objectName) returns view.name.startsWith(prefix) ? …. A view CONTAINER has no own name: the registry files it under the object it binds to (deriveViewContainerObject). On an artifact boot, the by-name read serves the container from the metadata service, and the translation of the served document reaches view.name as undefined. The config boot answers from a different registry copy.
Reach: every artifact boot (the production shape) serving a defineView container, on the by-name view read. The list read and the expanded views (<object>.<key>, which carry a name) are not measured as affected.
Expected (shape for triage, not a spec): the by-name read of a container answers 200 with the container on both doors. Either the translate path does not treat a container as a single view, or it keys the container by its object.
- Pin: an artifact boot and a config boot of one fixture answer the same for
GET /meta/view/<object>. - Control: an expanded view's by-name read is unchanged.
Related, closed: #20730 (the dashboard reads lacked a packaged base) and #20731 (an org overlay on a packaged view label). Neither is this crash.
Duplicate check
REST GET /repos/objectstack-ai/objectstack/issues?state=all&since=2026-09-15 was paged to the end: 2,561 issues, PRs excluded, closed included, #1883 to #22611. (REST search answers 403 in this container.) A local case-insensitive grep found:
viewTranslationKey: 0 hits;reading 'startsWith': 0 hits;meta/view/within 200 characters of500: 0 hits;translateView/translateMetaDocument: 4 hits (i18n: nothing readsTranslationData.flows— a screen flow still renders its authored English in every locale (the runner half #7646 deferred) #11287, rest: the /meta dashboard item and list reads pass no packaged base to translateDashboard, so a published org overlay on a translated dashboard keeps the shipped widget title — per-layer child of #20680 #20730, i18n: a published org overlay on a packaged view label is served to a zh-CN reader as the catalog translation (进行中), not the edit — the view twin of #20680 #20731, finding(spec,i18n):translateObjectnever reads an object's embeddedlistViews, so their labels are served in the authored language whileos i18n extractwrites_viewskeys for them #21006, all closed), none of them this crash.
Dedupe words: meta view by-name 500 · viewTranslationKey startsWith undefined · artifact boot view container read 500 · translateView container without name
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 10, 2026
Triage: first grade,
bug·priority:p2·domain:cli·area:api·pm:queueTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-10T06:01Z. ⛔ Not a claim, ⛔ not a dispatch.packages/rest(translateMetaDocument), sodomain:cli. If the fix touchesviewTranslationKeyinpackages/spec, the claim declares that file.500on the production boot shape, for everydefineViewcontainer.name.view.name?.. That would leave a container silently untranslated, a fault wearing the look of success.200forGET /meta/view/<object>, with labels translated. Control: an expanded view's by-name read is unchanged.objectstack-fleet commentedon Oct 10, 2026
Claim: PM loop round 1
Session:
session_01HGa2XMKBRUqPrZFxQavBwHAccount:
marchtian(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-22614-meta-view-container-readWorktree:
objectstack-issue-22614Domain:
domain:cliSeat:
domain:cli#2File surface (read at
origin/maind85615ddd9, 2026-10-10T08:51Z):packages/rest/src/meta-item-read-gate.ts:translateMetaDocument(:2237) classifies the served view document once, as a container or a single view, and translates each through its own key rule.200forGET /meta/view/<object>, with labels translated; control: an expanded view's by-name read is unchanged. They go where the dev finds the boot-parity tests, plus.changeset/22614-*.md(@objectstack/restpatch).packages/rest/src/rest-server.ts's/metacall site (:3795) only if the classification cannot live in the gate. PR feat(metadata-protocol,objectql,rest,spec)!: every metadata read is environment → code; legacy organization rows and sealed overlays are reported at boot, not served (#15206 S5) #22628 edits that region, so it is declared in the report first.packages/spec, includingviewTranslationKeyandtranslateViewinpackages/spec/src/system/i18n-resolver.ts, which aredomain:spec's. If the fix needs that file, the dev stops and reports, and this seat routes that half to the spec seat. ⛔ Notview.name?.(triage6094457162).Stop on breach; explain in the report.
Container & model:
M,mode:subagent,model: default (opus). Not mechanical: the read has to classify a document two boots serve from different registries.dispatch-gates --tier --repo objectstack-ai/objectstackprints "no path-derived mandate" for the gate file.Clause-②: no
A
500is a fault, not a refusal: every gate already accepts this read, and the config boot answers200for the same input. No accepted set, export or error code changes.Responsibility:
translateMetaDocumenthands a view container to the single-view translator, which reads the container's absentname| none | every artifact boot (the production shape) that serves adefineViewcontainer, on the authenticated by-name view readThread-read: 6094457162
Serial constraints cleared: none on the gate file. No open PR touches
meta-item-read-gate.ts. PR #22628 (#15206 S5, draft) editsrest-server.ts; this claim touches it only by the declare-first route above. PR #22612 (#22521,domain:cliseat 1) changes how a multi-package config boot registers its unowned top level; this card's fixture is single-package, and the pin is re-run after amainmerge once PR #22612 lands. PR #22421 editsi18n-resolver.test.tsonly.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
os-dev-report
{ "issue": 22614, "status": "done", "branch": "claude/issue-22614-meta-view-container-read", "pr": "https://github.com/objectstack-ai/objectstack/pull/22687", "session": "session_01HGa2XMKBRUqPrZFxQavBwH", "premise_still_valid": true, "summary": "Re-measured on base 3d0eeefa4: an artifact boot of a single-package project answered 500 on GET /api/v1/meta/view/acme_note (reading 'startsWith'); the config boot answered 200 but served a fabricated label: 'acme_note' with both views inside the container untranslated for zh-CN. The two documents differ because the config boot's ObjectQL boot loop stamps name onto the container (objectql/src/engine.ts:7575) while the artifact loader registers it as shipped (metadata/src/plugin.ts:1198); the read handed either to the spec's single-view translator. Fix lives in packages/rest/src/meta-item-read-gate.ts only: translateMetaDocument classifies a view document once (isAggregatedViewContainer); a container goes to translateMetaViewContainer, which translates each view it expands to (expandViewContainer) through translateView under its own qualified name and packaged base, lands the result in its slot, keys the container's own label/description by its bound object (objects.OBJECT._views.OBJECT), and drops translateView's name fallback so nothing is invented. packages/spec and rest-server.ts untouched. Dependency: @objectstack/metadata moved from devDependencies to dependencies of @objectstack/rest — this edit IS on disk and committed in 6fcd4edcd (packages/rest/package.json + pnpm-lock.yaml importer entry); the clean tree the PM read is the committed state, not an absent edit. What imports it at runtime: (1) this PR's deriveViewContainerObject from @objectstack/metadata/view-container in meta-item-read-gate.ts; (2) pre-existing, already on base: rest's dist bundles its devDependency @objectstack/objectql (rest-server.ts's optional await import('@objectstack/objectql')) plus metadata-protocol sections, whose external imports are hoisted to rest's top level — measured on the rebuilt dist: @objectstack/metadata/errors x5, /view-container x2 (1 mine, 1 objectql section), /view-container-name x2 (metadata-protocol + objectql sections); objectql's own dist carries the same three specifiers. So rest already loaded @objectstack/metadata at module load without declaring it; the move declares it.", "tests": "All at 7c71a9e34 (branch merged with origin/main ee3ae0360) unless noted. (1) Function pin packages/rest/src/meta-view-container-i18n.test.ts: 'Tests 10 passed (10)'. (2) Boot pin packages/cli/test/serve-view-container-read.integration.test.ts (integration project, spawns os serve; one fixture, config boot + os build + artifact boot): 'Tests 6 passed (6)'; against rest dist built at base 3d0eeefa4: 'Tests 4 failed | 2 passed (6)' — artifact 'expected 500 to be 200', config 'expected All Notes to be 全部笔记', parity mismatch; CONTROL and config-en green. (3) Tier partition pin test/vitest-tiers-partition.test.ts: 22 passed. (4) Ablation from committed fix f63e6a1d1 via scripts/ablation-replace.mjs (anchor \"if (metaType === 'view') {\" 1→0, blob fa91bb79→cf670c38): function pin on mutated src 'Tests 10 failed (10)'; rest rebuilt, ablation-dist-preflight '✓ dist/: marker present in 2 built files', boot pin 'Tests 4 failed | 2 passed (6)'; restore blob == HEAD fa91bb79, git diff HEAD empty, rebuilt, preflight --absent '✓ dist/: marker absent from all 6 built files' + '✓ tree: working tree clean', boot pin 'Tests 6 passed (6)'. A first dist-leg attempt is VOID (rebuild got verify-lock exit 99, preflight reported the marker absent; stopped, restored, not counted). (5) pnpm --filter @objectstack/rest typecheck: exit 0, check:test-typecheck OK (0 debt). (6) @objectstack/rest full vitest run (both projects) at f63e6a1d1: 'Test Files 275 passed (275) / Tests 5180 passed | 327 skipped (5507)', exit 0 — not re-run at 7c71a9e34: the merge brought no rest/metadata/metadata-protocol change (objectql change is file-field hydration). (7) Runtime dispatcher consumers (meta-item-read-gate-parity, meta-item-envelope, meta-list-projection-parity, meta-read-org-scope-parity, meta-package-all-fold): 'Test Files 5 passed (5) / Tests 793 passed (793)'. packages/cli unit tier beyond the partition pin: declared to CI (diff adds only an integration-tier test file).", "gates": [ "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-fixed.mjs :: 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-osv-exemptions.mjs :: exit 0", "node scripts/check-osv-exemptions.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-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/osv-base-relative.mjs --self-test :: 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 --filter @objectstack/spec run check:llms-txt :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:cli-test-child-env :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:dispatcher-error-vocabulary :: 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 0 (first run exit 3 PREREQUISITE NOT MET: 9 unbuilt dists; rerun of the main check exit 0, self-test passed in run 1)", "pnpm check:engine-double-contract :: exit 0", "pnpm check:error-status-conformance :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:manifest-repository-directory :: exit 0", "pnpm check:merge-driver :: 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:override-consistency :: 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 (first run exit 124 at the 420s runner cap after its self-test passed; rerun of the ratchet exit 0 in 406s)", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:service-providers :: 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:turbo-task-graph :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:type-check-debt :: exit 0", "pnpm check:vendor-export-contract-resolve :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0", "pnpm check:workspace-manifest-cycles :: exit 0", "pnpm lint (lane addition) :: proven narrowing, exit 0 — eslint --no-inline-config --format json packages/rest/src packages/cli/test/serve-view-container-read.integration.test.ts at 7c71a9e34: 303 files linted, 0 ignored, 0 errors, 0 warnings; population = eslint.config.mjs files globs (packages/**/*.{ts,tsx,mts,cts}, **/*.{ts,…}), all 3 touched .ts files in the linted set; invariance: no type-aware linting (all 8 parserOptions are {ecmaVersion, sourceType}, no project) and the config reads only its own two baselines, untouched — full pnpm lint is CI's" ], "gates_reconciliation": "node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack at 7c71a9e34 (change set: 6 paths vs merge base ee3ae0360, +722/-4): 'Run reconciliation — 76 derived, 76 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. Re-derived after the diff with no paths: 27 families more than the dispatch-time list (the changeset, lockfile and rest package.json pull in the changeset, OSV, manifest and type-check families); all run.", "line_budget": "n/a", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "3 — each a relay stroke (POST /repos/objectstack-ai/objectstack/dispatches from the session, executed as objectstack-fleet[bot]): ① pr_create → POST /repos/objectstack-ai/objectstack/pulls (run 38057227984; read-back 12201 bytes sent = stored) ② label-write --assign marchtian → POST /repos/objectstack-ai/objectstack/issues/22687/assignees (run 38057268459; read-back matches) ③ this os-dev-report → POST /repos/objectstack-ai/objectstack/issues/22614/comments. Plus git push (not REST). No labels written: none named by the dispatch, skip-changeset not applicable (rest publishes).", "files_changed": [ ".changeset/22614-meta-view-container-read.md", "packages/rest/src/meta-item-read-gate.ts", "packages/rest/src/meta-view-container-i18n.test.ts", "packages/rest/package.json", "pnpm-lock.yaml", "packages/cli/test/serve-view-container-read.integration.test.ts" ], "deviations": [ "Role file wins on attribution: commits carry the model-free pair (Claude-Session + Co-authored-by: Claude) and the PR body ends with the AGENTS session-URL footer; the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer — not used.", "The merge commit 7c71a9e34 was amended locally (before its first push) to add the trailer pair.", "Boot pin lives in a new sibling file: main has no artifact-vs-config parity file yet (PR #22612 adds serve-config-boot-residual.integration.test.ts, still a draft); it reuses the same helpers.", "Container's own label/description key objects.OBJECT._views.OBJECT follows Zone 3's suggestion; see open_questions.", "Before the PM's resume, several locked runs were started with nohup and awaited in the foreground with tail --pid (the role file's form). After the resume, gate batch 3 outran the 600s tool cap and the harness moved it to the background; I waited on its PID in the foreground and read its exit file, not a wake-up.", "Two gates needed a second run (check:dual-build-cjs-loads PREREQUISITE NOT MET, then exit 0; check:query-options-erasure cut by my 420s runner cap after its self-test passed, then the ratchet exit 0); both recorded in gates.", "Full rest suite measured at f63e6a1d1, pins/typecheck/runtime consumers re-measured at the merged head 7c71a9e34 (AGENTS Multi-agent §10 scoping).", "PR #22612 interplay checked by reading its diff and its own single-package CONTROL, not by a merged run — NOT MEASURED: this pin on a merge of #22612." ], "open_questions": [ { "question": "A view container's own label/description is now read from objects.OBJECT._views.OBJECT — the key the config boot already read for a container named after its object and the one the i18n extractor scaffolds for a named container, but one the spec resolver's header (i18n-resolver.ts key list) does not declare for containers. Declare it or retire it?", "options": [ "A — domain:spec declares it in the i18n-resolver key list (and the extractor scaffolds it for nameless containers too)", "B — retire it: a container's own label gets no catalog address, and this read leaves it as authored" ], "recommendation": "A, lightly: the key already has a producer (the extractor's named-container branch) and a reader since before this card, so declaring it is a documentation act rather than new surface; it can collide only with a listViews key equal to the object name. Not blocking — the PR serves correct answers either way." } ], "out_of_scope_findings": [ "carrier: none (承接者:无) · noted, not filed (PR Acceptance notes) — rest's dist bundles its devDependency @objectstack/objectql (and metadata-protocol sections, ~107k lines) through rest-server.ts's try { await import('@objectstack/objectql') } catch, whose comment says a deployment without the data engine must degrade; bundled, the import never fails and a second objectql copy ships inside rest. No wrong answer reproduced. dedupe words: rest dist bundles objectql · optional import bundled devDependency · rest-server legalNextStates import", "carrier: none (承接者:无) · noted, not filed (PR Acceptance notes) — packages/cli/src/utils/i18n-extract.ts's top-level view walk sends a container carrying its own name down the single-view branch: it scaffolds _views.NAME and none of its views' keys (default list, listViews). Not exercised through a public door. dedupe words: i18n extract named view container · pushViewEntries container name branch · missing view keys scaffold", "Known limit of this PR (in its Acceptance notes and the function docblock): a container saved at runtime on another package's object is expanded by the protocol under its own name (OBJECT.NAME.KEY); the gate translates its views under the composer's names." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
ACCEPT: PR #22687 (head
7c71a9e34) for #22614domain:cliseat 2 (#22648) ·session_01HGa2XMKBRUqPrZFxQavBwH· 2026-10-10T13:55Z.Thread-read: 6098190980
The seat read the PR on GitHub at
7c71a9e34, not the report's account of it.Checklist:
main, first lineFixes #22614, a line-initialClause-②: no. The body carries no other closing keyword.check-governed-merges --pr 22687: NOT governed, 726 changed lines, under 3000. ⛔ Nopackages/spec, norest-server.ts, nocontent/docs/releases/.translateMetaDocumentclassifies aviewdocument once (isAggregatedViewContainer).translateMetaViewContainer, which translates each expanded view throughtranslateViewunder its own name and packaged base.view.name?.guard.@objectstack/restpatch, every bullet matches the diff.500→200;labelequal to the object name;objects.OBJECT._views.KEY;objects.OBJECT._views.OBJECT;@objectstack/metadatais now a runtime dependency of@objectstack/rest, because the gate importsderiveViewContainerObjectfrom@objectstack/metadata/view-container.packages/metadata'sexportsonorigin/main.@objectstack/metadatadoes not depend on@objectstack/rest, so there is no cycle.packages/cli/test/serve-view-container-read.integration.test.ts) is 6/6 green, and 4 of 6 red against baserest(expected 500 to be 200,expected All Notes to be 全部笔记);dist/with the preflight marker present, then absent on restore.7c71a9e34, read at this stamp: 16 success, 2 skipped, 14 in progress, 0 failed. Landing waits for every check green.Deviations accepted:
packages/rest/package.json,pnpm-lock.yaml, and the boot pin inpackages/cli/test/. All three follow from the fix and are stated in the PR.mainhas no artifact-vs-config parity file yet; PR fix(metadata,runtime): a multi-package config boot registers its unowned top level under manifest.id, as the artifact boot does #22612 adds one.Report items:
objects.OBJECT._views.OBJECT): Acceptance notes.packages/spec/src/system/i18n-resolver.ts's key list is adomain:specdocumentation act, not a defect. The docs follow the implementation.@objectstack/objectql): Acceptance notes. No wrong answer was reproduced at a public door.i18n-extract.tssends a named container down the single-view branch): Acceptance notes. It is not exercised through a public door.Next: this seat flips the PR to ready and arms auto-merge once every check is green on the head, then follows it to MERGED.
Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Landed: PR #22687 merged through the merge queue at 2026-10-10T14:51Z as
4b7c6304b, a single-parent squash onmain, read withgit rev-list --parents.domain:cliseat 2 (#22648) ·session_01HGa2XMKBRUqPrZFxQavBwH· 2026-10-10T17:46Z.The card closed
completedwith the merge.pm:dispatchedwas stripped by a targeted delete; the domain, area and type labels stay.Lane re-check after the merge: no other card was closed by this PR. Its body carries one closing keyword,
Fixes #22614.Left for later, per the ACCEPT (
6098220162), not owed by this card:objects.OBJECT._views.OBJECTinpackages/spec/src/system/i18n-resolver.ts's key list (domain:spec);@objectstack/objectql;i18n-extract.ts's named-container branch.All three are in the PR's Acceptance notes.
Generated by Claude Code