Summary
Backend status and channel services return engine-neutral Status[] and ChannelMessage[] values without remapping, but the typed SDKs declare different fields. StatusRecord omits contact, caption, mediaUrl, backgroundColor, font, and expiresAt while advertising nonexistent statusId/body fields. ChannelRecord substitutes pictureUrl/role for backend picture/verified/inviteCode/createdAt, and the channel-messages endpoint is typed as the unrelated persisted MessageRecord. Java repeats the incompatible channel model.
Impact: SDK users receive runtime objects whose documented type omits real response fields and exposes fields that the server never returns. In particular, JavaScript channel messages are presented as persisted MessageRecord objects with sessionId, chatId, direction, and status even though the endpoint returns ChannelMessage objects containing hasMedia/mediaUrl. This causes incorrect autocomplete, compile-time rejection of valid field access, and successful compilation of accesses that evaluate to undefined.
Code path
Checked against current main at commit 113c4207a08b3f17e5894cef3eda9c80d6abee7d.
src/engine/interfaces/whatsapp-engine.interface.ts:230-252
src/engine/interfaces/whatsapp-engine.interface.ts:271-289
src/modules/status/status.service.ts:9-15
src/modules/channel/channel.service.ts:33-34
sdk/javascript/src/types.ts:503-515
sdk/javascript/src/types.ts:612-634
Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e 'const fs=require("fs"); const fields=(p,n)=>{const s=fs.readFileSync(p,"utf8"); const t=s.slice(s.indexOf(`export interface ${n} {`)+`export interface ${n} {`.length); const b=t.slice(0,t.indexOf("\n}")); return [...b.matchAll(/^ ([A-Za-z][A-Za-z0-9]*)\??:/gm)].map(x=>x[1])}; const server="src/engine/interfaces/whatsapp-engine.interface.ts", sdk="sdk/javascript/src/types.ts"; for (const [a,b] of [["Label","LabelRecord"],["Channel","ChannelRecord"],["ChannelMessage","MessageRecord"],["Status","StatusRecord"]]) { const x=fields(server,a), y=fields(sdk,b); console.log(`${a}->${b} server-only=${x.filter(k=>!y.includes(k)).join(",")||"none"} sdk-only=${y.filter(k=>!x.includes(k)).join(",")||"none"}`) }'
Observed output:
Label->LabelRecord server-only=hexColor sdk-only=color,colorHex
Channel->ChannelRecord server-only=inviteCode,picture,verified,createdAt sdk-only=pictureUrl,role
ChannelMessage->MessageRecord server-only=hasMedia,mediaUrl sdk-only=sessionId,waMessageId,chatId,from,to,type,direction,metadata,status,createdAt
Status->StatusRecord server-only=contact,caption,mediaUrl,backgroundColor,font,expiresAt sdk-only=statusId,body
Neighboring control:
node -e 'const fs=require("fs"); const fields=(p,n)=>{const s=fs.readFileSync(p,"utf8"); const t=s.slice(s.indexOf(`export interface ${n} {`)+`export interface ${n} {`.length); const b=t.slice(0,t.indexOf("\n}")); return [...b.matchAll(/^ ([A-Za-z][A-Za-z0-9]*)\??:/gm)].map(x=>x[1])}; const server="src/engine/interfaces/whatsapp-engine.interface.ts", sdk="sdk/javascript/src/types.ts"; for (const [a,b] of [["Catalog","CatalogInfo"],["Product","CatalogProduct"]]) { const x=fields(server,a), y=fields(sdk,b); console.log(`${a}->${b} server-only=${x.filter(k=>!y.includes(k)).join(",")||"none"} sdk-only=${y.filter(k=>!x.includes(k)).join(",")||"none"}`) }'
Control output:
Catalog->CatalogInfo server-only=none sdk-only=none
Product->CatalogProduct server-only=none sdk-only=none
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
Typed SDK engine-resource models use missing or incorrect fields for direct engine-neutral payloads. B0121 and B0127 overlap on the same label/channel model drift and are resolved by one coordinated wire-model alignment.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Align JavaScript and Java engine-resource response models with the directly returned Contact, Status, Label, Channel, and ChannelMessage wire shapes.
Suggested tests
- Add a regression test for the reproduction above.
- Add a neighboring control assertion so the intended non-bug path remains covered.
Submitted with Codex.
Summary
Backend status and channel services return engine-neutral Status[] and ChannelMessage[] values without remapping, but the typed SDKs declare different fields. StatusRecord omits contact, caption, mediaUrl, backgroundColor, font, and expiresAt while advertising nonexistent statusId/body fields. ChannelRecord substitutes pictureUrl/role for backend picture/verified/inviteCode/createdAt, and the channel-messages endpoint is typed as the unrelated persisted MessageRecord. Java repeats the incompatible channel model.
Impact: SDK users receive runtime objects whose documented type omits real response fields and exposes fields that the server never returns. In particular, JavaScript channel messages are presented as persisted MessageRecord objects with sessionId, chatId, direction, and status even though the endpoint returns ChannelMessage objects containing hasMedia/mediaUrl. This causes incorrect autocomplete, compile-time rejection of valid field access, and successful compilation of accesses that evaluate to undefined.
Code path
Checked against current
mainat commit113c4207a08b3f17e5894cef3eda9c80d6abee7d.src/engine/interfaces/whatsapp-engine.interface.ts:230-252src/engine/interfaces/whatsapp-engine.interface.ts:271-289src/modules/status/status.service.ts:9-15src/modules/channel/channel.service.ts:33-34sdk/javascript/src/types.ts:503-515sdk/javascript/src/types.ts:612-634Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e 'const fs=require("fs"); const fields=(p,n)=>{const s=fs.readFileSync(p,"utf8"); const t=s.slice(s.indexOf(`export interface ${n} {`)+`export interface ${n} {`.length); const b=t.slice(0,t.indexOf("\n}")); return [...b.matchAll(/^ ([A-Za-z][A-Za-z0-9]*)\??:/gm)].map(x=>x[1])}; const server="src/engine/interfaces/whatsapp-engine.interface.ts", sdk="sdk/javascript/src/types.ts"; for (const [a,b] of [["Label","LabelRecord"],["Channel","ChannelRecord"],["ChannelMessage","MessageRecord"],["Status","StatusRecord"]]) { const x=fields(server,a), y=fields(sdk,b); console.log(`${a}->${b} server-only=${x.filter(k=>!y.includes(k)).join(",")||"none"} sdk-only=${y.filter(k=>!x.includes(k)).join(",")||"none"}`) }'Observed output:
Neighboring control:
node -e 'const fs=require("fs"); const fields=(p,n)=>{const s=fs.readFileSync(p,"utf8"); const t=s.slice(s.indexOf(`export interface ${n} {`)+`export interface ${n} {`.length); const b=t.slice(0,t.indexOf("\n}")); return [...b.matchAll(/^ ([A-Za-z][A-Za-z0-9]*)\??:/gm)].map(x=>x[1])}; const server="src/engine/interfaces/whatsapp-engine.interface.ts", sdk="sdk/javascript/src/types.ts"; for (const [a,b] of [["Catalog","CatalogInfo"],["Product","CatalogProduct"]]) { const x=fields(server,a), y=fields(sdk,b); console.log(`${a}->${b} server-only=${x.filter(k=>!y.includes(k)).join(",")||"none"} sdk-only=${y.filter(k=>!x.includes(k)).join(",")||"none"}`) }'Control output:
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
Typed SDK engine-resource models use missing or incorrect fields for direct engine-neutral payloads. B0121 and B0127 overlap on the same label/channel model drift and are resolved by one coordinated wire-model alignment.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Align JavaScript and Java engine-resource response models with the directly returned Contact, Status, Label, Channel, and ChannelMessage wire shapes.
Suggested tests
Submitted with Codex.