Repository navigation
DraftChangesPanel interpolates the stored type verbatim into /meta item routes — fold to singular before interpolating (step ③ residue of objectstack#9180) #5356
Description
Activity
os-support-ai commented
on Aug 20, 2026 CollaboratorMore actionsClaim: PM loop round 18
Session:session_01RV6yuVCxymHYE16PL9vQkE
Branch:claude/issue-5356-draftchanges-meta-singular
Worktree:objectui-issue-5356
Domain:repo:objectui(execution seat)
File surface:packages/app-shell/src/preview/DraftChangesPanel.tsxand its test (stop on breach; explain in the report)
Container & model: S,mode:subagent,model: opus—node scripts/pm/dispatch-gates.mjs --tier <paths>returns no path-derived mandate; tier is this seat's call.
Clause-②: no — internal spelling correction on an outbound route; nothing about what the server accepts changes, per the card and the ruling it carves.
Serial constraints cleared:packages/app-shell/src/preview/has no in-flight claim. The app-shell subdirectory-level serial exemption applies and does not spill: the last app-shell PR to land (PR5355, #5228) was insrc/views/metadata-admin/, and #5357 (app-shell ESM specifiers) ispm:blocked, not dispatched. Round-18 siblings hold disjoint surfaces.Premise re-verified on
origin/mainbdf8cf76e, 2026-08-20 — all three sites present at the stated lines (:62type: d.type as string,:76and:112the two interpolations), plus:49/api/v1/meta/_draftsas the feed they come from.The ruling is not re-litigable. objectstack-ai/objectstack#9180, maintainer 2026-08-16: the
/metatype segment is singular, always. Step ③ was scoped by the 2026-08-17 re-weigh to internal spelling correction across the three self-owned repos only, verbatim, untranslated: 「② 照原样做;只需要修正 objectstack objectui cloud 中错误的写法。」 You implement it; you do not re-open it.What "fold to singular" has to mean here
The panel is data-driven, not authored —
typearrives from the/api/v1/meta/_draftsfeed's stored rows with only atypeofcheck. So this is not a find-and-replace of a literal: it is a normalization at the panel boundary, applied before the value reaches any/metaitem route.Two things to get right, and both are places where a plausible-looking fix is wrong:
- Fold, don't strip. Whatever helper you use must be the repo's existing singular-folding rule if one exists — look for it before writing a new one, and say in the PR which you found and why you used it. A hand-rolled
replace(/s$/, '')is wrong for any type whose singular already ends ins. If no shared helper exists, say so explicitly; do not silently invent one and present it as the convention. - Do not launder the at-rest data. Pre-docs(app-shell): page-route action examples taught the unreadable nested
actionbag #7894 plural residue stored on the server stays unreachable; this change makes the Console stop emitting plural, it does not migrate anything. ⛔ Do not add a write path, a migration, or a server-side tolerance.
Reverse-verification
Pin the behaviour, then revert your normalization and show the pin goes red — a route-spelling fix is exactly the kind that passes vacuously if the fixture feeds an already-singular type. State whether a build artifact sits between your edit and the thing under test, per leg.
Part of objectstack-ai/objectstack#9180— ⛔ notFixesfor the objectstack card; useFixes #5356for this one.
Generated by Claude Code
- Fold, don't strip. Whatever helper you use must be the repo's existing singular-folding rule if one exists — look for it before writing a new one, and say in the PR which you found and why you used it. A hand-rolled
os-support-ai commented
on Aug 20, 2026 CollaboratorMore actionsos-dev-report
{ "issue": 5356, "status": "done", "branch": "claude/issue-5356-draftchanges-meta-singular", "pr": "https://github.com/objectstack-ai/objectui/pull/5361", "premise_still_valid": true, "summary": "Re-verified on origin/main bdf8cf76e: all four measured sites present at the stated lines, and :76/:112 are still the only variable-typed /meta route emitters in objectui (independent grep: every other site interpolates a name into a literal singular type). The stored `type` is now folded once, at the boundary where _drafts rows become DraftChangeEntry, with @objectstack/spec/shared's canonicalMetaUrlType — the /meta-specific fold (objectstack#7894/#8424), not pluralToSingular, whose keys are defineStack() collection properties and which is missing field/seed/external_catalog/translation. Both item routes, the grouping and the type heading read that one folded value. Emit-side only: no write path, no migration, no server-side tolerance. DESIGN NOTE THE PM SHOULD SEE: my first implementation mirrored the 35-entry table locally with a parity test, to avoid the measured +213.4 KB min / +60.1 KB gz this import adds to the console's EAGER graph (the panel is static from ConsoleLayout, and the vendor-objectstack chunk group makes a dynamic import ineffective — objectui#5266). scripts/check-spec-symbol-derivation.mjs refuses that mirror by design (a spec-named symbol must be derived; a faithful copy is the fork it exists to prevent), so the import is the sanctioned form and the weight is filed as #5359, whose levers live in apps/console/vite.config.ts — outside this card's file surface. File surface honoured: only DraftChangesPanel.tsx, its test, and the changeset.", "tests": "All from the repo root at 2efc90d19, heavy runs under flock /tmp/os-heavy-verify.lock. (1) `pnpm exec vitest run packages/app-shell/src/preview/__tests__/DraftChangesPanel.test.tsx` -> 'Test Files 1 passed (1) / Tests 13 passed (13)' (8 pre-existing + 5 new route pins). (2) `pnpm --workspace-concurrency=2 --filter @object-ui/app-shell type-check` -> exit 0 with the script echoed (`tsc --noEmit && tsc -p tsconfig.test.json`), after `pnpm --filter '@object-ui/app-shell^...' build`; before that build it failed with TS2307 on every workspace dep, i.e. the run was real, not a zero-match pass. (3) eslint on both changed files -> 0 errors, 2 pre-existing warnings on untouched lines. (4) Gates at this sha, all exit 0: check-spec-symbol-derivation, check-control-bytes, check-changeset-presence, check-changeset-no-major, check-phantom-dependencies, check-package-self-import. REVERSE VERIFICATION: reverting the fold to `type: d.type as string` turned 4 of the 5 route pins red -- \"expected [ '/api/v1/meta/objects' ] to include '/api/v1/meta/object'\", \"expected [ '/api/v1/meta/capabilities' ] to include '/api/v1/meta/capability'\", \"expected 'objects · 1' to be 'object · 1'\" -- and restoring returned 13/13. The 5th pin (already-canonical type + plugin kind pass through) is green in both legs by design; it is the passthrough control. NO BUILD ARTIFACT SITS BETWEEN THE EDIT AND THE SUBJECT ON EITHER LEG: the test imports ../DraftChangesPanel by relative path and vitest transforms it from source, so neither leg needed a rebuild; the only prebuilt artifact in play is the installed @objectstack/spec dist that provides the fold, which the ablation does not touch. Ran the ablation from the committed state, so both readings are reproducible from git.", "open_questions": [ { "question": "The fix knowingly adds +60.1 KB gzipped to every console page load (measured) to fold a route segment an admin-only panel emits. Ship as-is, or pay it down?", "options": [ "A - Ship as-is (this PR). The spec-derivation gate refuses a local mirror; the weight is filed as #5359 and recorded at the import site so it is not invisible.", "B - Follow up on #5359: exclude the spec `shared` subpath from VENDOR_OBJECTSTACK_TEST and switch this fold to `await import()` inside the async boundary that already exists, with a build assertion in the spirit of assertLazyLinterStaysLazy. Both halves are in apps/console/vite.config.ts, outside this card's surface.", "C - Ask upstream for a finer-grained spec export for the /meta spelling contract, which would make the trade disappear for every consumer." ], "recommendation": "A now, B or C as a separate card. A is the only option inside this card's file surface, and it is the one the repo's own gate sanctions; the bytes are recorded rather than silent, which is what #5324 says the console's budget cannot do on its own." } ], "out_of_scope_findings": [ "filed as #5359: the /meta fold puts @objectstack/spec/shared on the console's eager graph (+213.4 KB min / +60.1 KB gz, measured); the vendor-objectstack chunk group makes lazy loading ineffective, and no budget weighs those bytes (related: #5324, #5325, objectui#5266)", "not filed, already tracked: #4940 covers the sibling family in data-objectstack (adapter meta call sites passing a plural type argument through the client). Out of this card's file surface; no duplicate opened. Note for the PM: the card's premise says objectui has 'zero literal plural routes', which my own grep confirms for raw fetch URLs -- #4940's sites are plural TYPE ARGUMENTS handed to the metadata client, not literal plural URLs, so the two statements do not conflict." ] }
Generated by Claude Code
os-support-ai commented
on Aug 20, 2026 CollaboratorMore actionsos-dev-report
Supersedes the report in comment 5350067829 — same delivery, with the PM's subpath-reachability question answered (
testsandopen_questionsupdated; no code change, still2efc90d19).{ "issue": 5356, "status": "done", "branch": "claude/issue-5356-draftchanges-meta-singular", "pr": "https://github.com/objectstack-ai/objectui/pull/5361", "premise_still_valid": true, "summary": "Re-verified on origin/main bdf8cf76e: all four measured sites present at the stated lines, and :76/:112 are still the only variable-typed /meta route emitters in objectui. The stored `type` is folded once, at the boundary where _drafts rows become DraftChangeEntry, with @objectstack/spec/shared's canonicalMetaUrlType — the /meta-specific fold (objectstack#7894/#8424), not pluralToSingular, whose keys are defineStack() collection properties and which is missing field/seed/external_catalog/translation. Both item routes, the grouping and the type heading read that one folded value. Emit-side only: no write path, no migration, no server-side tolerance. The import costs a measured +213.4 KB min / +60.1 KB gz on the console's EAGER graph; a local mirror of the table was tried first and is refused by scripts/check-spec-symbol-derivation.mjs by design, and the PM's follow-up question (is the fold reachable from a subpath already on the eager baseline?) is now answered NO with counter-probed evidence — so the import is the only in-surface spelling and the weight is filed as #5359. File surface honoured: only DraftChangesPanel.tsx, its test, and the changeset.", "tests": "All from the repo root at 2efc90d19, heavy runs under flock /tmp/os-heavy-verify.lock. (1) `pnpm exec vitest run packages/app-shell/src/preview/__tests__/DraftChangesPanel.test.tsx` -> 'Test Files 1 passed (1) / Tests 13 passed (13)' (8 pre-existing + 5 new route pins). (2) `pnpm --workspace-concurrency=2 --filter @object-ui/app-shell type-check` -> exit 0 with the script echoed, after `pnpm --filter '@object-ui/app-shell^...' build`; before that build it failed with TS2307 on every workspace dep, so the run was real, not a zero-match pass. (3) eslint on both changed files -> 0 errors, 2 pre-existing warnings. (4) Gates at this sha, all exit 0: check-spec-symbol-derivation, check-control-bytes, check-changeset-presence, check-changeset-no-major, check-phantom-dependencies, check-package-self-import. REVERSE VERIFICATION: reverting the fold turned 4 of the 5 route pins red -- \"expected [ '/api/v1/meta/objects' ] to include '/api/v1/meta/object'\", \"expected [ '/api/v1/meta/capabilities' ] to include '/api/v1/meta/capability'\", \"expected 'objects · 1' to be 'object · 1'\" -- and restoring returned 13/13; the 5th pin is the passthrough control, green in both legs by design. No build artifact sits between the edit and the subject on either leg: the test imports ../DraftChangesPanel by relative path and vitest transforms it from source; the only prebuilt artifact in play is the installed @objectstack/spec dist, which the ablation does not touch. SUBPATH REACHABILITY (PM follow-up, evidence in PR comment 5350102898): canonicalMetaUrlType / META_URL_TO_SINGULAR are exported by ./shared and by NO other entry in the package's 18-subpath exports map -- all 15 code entries grep 0/0 in both index.d.ts and index.mjs, including the /ui and /kernel baseline. Zeros counter-probed on the same files with symbols objectui imports from them today (expandViewContainer 1/5 hits, PageSchema 4, deriveNamespaceFromPackageId 2/2, composeStacks 10), and confirmed by resolving each entry at runtime and filtering its real export list: root 125 exports and /ui 218 exports carry nothing fold-shaped; /shared 71 exports carry the whole family. /kernel and root merely INLINE PLURAL_TO_SINGULAR without exporting it, and it is the wrong map regardless; deriving a fold from /kernel's DEFAULT_METADATA_TYPE_REGISTRY would mean rebuilding restPluralOfMetaType, which objectstack#8424 keeps module-internal on purpose. No deep-path import past the exports map was attempted.", "open_questions": [ { "question": "The fix adds a measured +60.1 KB gzipped to every console page load to fold a route segment an admin-only panel emits, and there is no cheaper import. Ship as-is, or pay it down later?", "options": [ "A - Ship as-is (this PR). The only in-surface spelling: a local mirror is refused by the spec-derivation gate, and no eager-baseline subpath exports the fold (measured above). The weight is recorded at the import site and filed as #5359.", "B - Follow up on #5359: exclude the spec `shared` subpath from VENDOR_OBJECTSTACK_TEST and switch this fold to `await import()` in the async boundary that already exists, with a build assertion in the spirit of assertLazyLinterStaysLazy. Both halves live in apps/console/vite.config.ts, outside this card's surface. This RELOCATES the bytes off the eager path.", "C - Ask upstream for a finer-grained export of the /meta spelling contract. The only option that REMOVES the cost rather than relocating it, and it removes it for every consumer." ], "recommendation": "A now, C as the better first move if #5359 is ever picked up. The subpath measurement closes the cheap-swap possibility, so nothing further is available inside this card's file surface." } ], "out_of_scope_findings": [ "filed as #5359: the /meta fold puts @objectstack/spec/shared on the console's eager graph (+213.4 KB min / +60.1 KB gz, measured); the vendor-objectstack chunk group makes lazy loading ineffective and no budget weighs those bytes (related: #5324, #5325, objectui#5266). Updated with the subpath-reachability measurement, which narrows its levers to relocate-vs-remove.", "not filed, already tracked: #4940 covers the sibling family in data-objectstack (adapter meta call sites passing a plural type argument through the client). Out of this card's file surface; no duplicate opened. The card's 'zero literal plural routes' premise holds for raw fetch URLs -- #4940's sites are plural TYPE ARGUMENTS handed to the metadata client, so the two statements do not conflict." ] }
Generated by Claude Code
- added a commit that references this issue
on Aug 23, 2026
Filed at destination by the objectstack spec seat carving the objectstack-ai/objectstack#9180 ruling, under the maintainer's direct reassignment (2026-08-19, verbatim: 「改派给 spec 席,现在就拆」; audit: objectstack-ai/objectstack#9180 comment 5349687344).
The ruling being implemented (⛔ not re-litigable)
The
/metatype segment is singular — always (objectstack-ai/objectstack#9180, maintainer 2026-08-16). The 2026-08-17 re-weigh scoped step ③ to internal spelling correction only across the three self-owned repos, verbatim, untranslated: 「② 照原样做;只需要修正 objectstack objectui cloud 中错误的写法。」Measured sites (2026-08-19, objectui
origin/main— re-verify at your branch point)packages/app-shell/src/preview/DraftChangesPanel.tsx::62—type: d.type as string(taken straight from the/api/v1/meta/_draftsfeed's stored rows;typeofcheck only, no fold):76—fetch(`/api/v1/meta/${encodeURIComponent(type)}`):112—`/api/v1/meta/${encodeURIComponent(type)}/${encodeURIComponent(name)}${qs}`The panel is data-driven, not authored: a stored plural row makes the Console emit a plural request. The #9180 sweep found this is the only variable-typed
/metaitem-route emitter in objectui (zero literal plural routes; 29 files with singular routes as the positive control).The fix shape
Fold/normalize the stored
typeto the singular spelling before interpolating into any/metaroute (a local normalization at the panel boundary; pre-#7894 at-rest plural residue stays unreachable and is not laundered by this). Internal correction only — nothing about what the server accepts changes.Dedup: objectui
search_issuesis known-broken (returns 0 for every query); checked vialist_issuestitle scan instead — no existing card names DraftChangesPanel or/metaspelling.Part of objectstack-ai/objectstack#9180.