Skip to content

refactor(rest): drop the (p as any) cast at the publishMetaItem call site (#11145) - #11682

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-11145-drop-publishmetaitem-cast
Aug 24, 2026
Merged

refactor(rest): drop the (p as any) cast at the publishMetaItem call site (#11145)#11682
huangyiirene merged 2 commits into
mainfrom
claude/issue-11145-drop-publishmetaitem-cast

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes#11145

The POST /meta/:type/:name/publish door reached its protocol method through
(p as any).publishMetaItem twice — once for the 501 feature-detection guard,
once for the call — so nothing checked the request literal it built. #11006
(maintainer ruling 2026-08-22, option B; landed as #11426) declared the member
on MetadataProtocol with a PublishMetaItemRequest, which is what takes the
prop out from under the cast. This is the consumer half the ruling routed to
this lane by name.

The premise held, and it was measured rather than assumed

Verified on origin/main @ c251ef4213 (the file took four commits on
2026-08-24, so line numbers were taken fresh):

  • packages/spec/src/api/protocol.zod.ts:2535 declares
    publishMetaItem?(request: PublishMetaItemRequest): Promise<PublishMetaItemResponse>
    — an optional member carrying the request type, not a narrowed variant.
  • Both cast sites were still live, at 5940 (the guard) and 6051 (the call).
  • packages/spec was rebuilt before any verdict was read: the emitted
    packages/spec/dist/api/index.d.ts carries the member at line 8987 and
    exports type PublishMetaItemRequest, so the readings below are against the
    rebuilt .d.ts and not a stale artifact. (packages/rest looks up
    @objectstack/spec through its exports map, i.e. dist/.)

What the cast was load-bearing for — three measured legs

All three legs are pnpm --filter @objectstack/rest typecheck (tsc --noEmit),
run back to back at a5d4f97b92 under the shared verify lock, with the mutation
confirmed on disk by grep between legs and restored from the commit under a
shell trap:

Leg 1 — cast removed (this PR's tree): clean, exit 0. On-disk before the
run: (p as any).publishMetaItem count 0, typed-literal count 1. No second,
independent reason for the cast surfaced. Had one, this PR would have been a
finding instead — re-casting in a different spelling to reach green would have
defeated the card.

Leg 2 — undeclared key injected at the call site: TS2353, exit 2. On-disk
before the run: injected probe count 1.

src/rest-server.ts(6059,25): error TS2353: Object literal may only specify known
properties, and 'undeclaredKeyProbe' does not exist in type
'TransportScopedMetaRequest<{ type: string; name: string; organizationId?: string
| undefined; actor?: string | undefined; message?: string | undefined; packageId?:
string | null | undefined; }>'.

That diagnostic is the deliverable, not a side effect: it is the compile
error the card names as its acceptance signal, and the expanded type in the
message is the spec-declared surface the literal is now judged against.

Leg 3 — restored: clean, exit 0. On-disk after restore: probe count 0,
typed-literal count 1. The mutation left nothing behind.

The historical claim in the retired docblock is reproduced rather than
re-measured, and it is the counter-intuitive half worth keeping: deleting the
cast while the member was undeclared answered TS2339: Property 'publishMetaItem' does not exist on type 'RestProtocol'not a TS2353
about an unknown key. The cast carried member existence, not request shape,
which is why widening the implementation's own parameter type in
@objectstack/metadata-protocol (a package packages/rest deliberately does
not depend on) could never have retired it, and why declaring the member did.

What changed

No behaviour change, and nothing about the wire moves. The outgoing payload
is byte-identical — same keys, same conditional spreads. The edit hoists the
literal into a const and drops a type-level cast.

No type-level pin, and the reason is measured

A @ts-expect-error pin in a packages/rest test would be inert today, so
it was deliberately not added rather than added for visibility.
packages/rest/tsconfig.json excludes **/*.test.ts / **/*.spec.ts, and the
repo runs no vitest typecheck mode — the package's tests sitting outside every
tsc program is a ledgered state, recorded as TEST_DEBT at 155 errors in
scripts/check-type-check-coverage.mjs. A pin nothing compiles is a phantom
check, which is worse than none. The guarantee is instead pinned where it is
actually compiled: the call-site literal in src/, which typecheck reads on
every run — that is what leg 2 exercised. The declaration side is already pinned
upstream by #11006 (expectTypeOf<MetadataProtocol['publishMetaItem']>() in
packages/spec/src/api/protocol.test.ts:1760).

Verification, all at a5d4f97b92

  • pnpm --filter @objectstack/rest typecheck — exit 0 (legs above).
  • pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 — the full
    package suite, Test Files 142 passed (142), Tests 2270 passed (2270).
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed) —
    exit 0.
  • All 17 gate families derived by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack from the actual change set — exit 0 each. Plus
    pnpm check:nul-bytes — exit 0.

packages/spec was not touched: it is off limits from this lane, and the
cleanup needed nothing further there.

Out of scope — filed, not touched here

Two sibling doors in this same file are in the state publishMetaItem just left,
both needing a packages/spec decision this lane cannot take. Filed rather than
touched; ⛔ neither is addressed here.

Neither is in this diff. Both are packages/spec decisions; #11678 and #11679
remain open for triage.


Generated by Claude Code

…all site
`MetadataProtocol` now declares `publishMetaItem` as an optional member
carrying `PublishMetaItemRequest` (#11006, maintainer ruling 2026-08-22,
option B), so the cast that was feature-detecting an ADR-0076 D9 server-only
extension has nothing left to carry. The request literal is typed through the
#9741 `TransportScopedMetaRequest` wrapper, which is the point: an undeclared
key at this call site is now a compile error instead of an unchecked payload
member. The docblock that existed only to explain why the cast had to stay is
replaced rather than left behind.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VK8rFDtg8eREaxBGX99Csn
…Item cast
Part of #11145
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VK8rFDtg8eREaxBGX99Csn
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/rest/src/rest-server.ts) — pages documenting those are invisible to this run

Coarse fallback — 13 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 88b9d749a1eb76b53966b0081006d2c5bccb341dpackageMentionDocs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tooling labels Aug 24, 2026
@huangyiirene
huangyiirene marked this pull request as ready for review August 24, 2026 12:18
@huangyiirene
huangyiirene added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit 6e33394Aug 24, 2026
32 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-11145-drop-publishmetaitem-cast branch August 24, 2026 12:36
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mtooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove the (p as any) cast at the REST publishMetaItem call site once MetadataProtocol declares the member (#11006 follow-up)

2 participants

@huangyiirene@claude