/* * 떨어진 것을 **보내는 쪽이 알아들을 모양으로** 바꾼다 — 순수 판정, 한 곳. * * ── 무엇이 있었나 (2026-09-07 측정) ───────────────────────────────────────── * 훅 응답의 떨어진 목록이 `{ record, errors }` 였고, plant 은 그것을 **봉투 id 문자열 목록**으로 * 읽고 있었다. * * ``` * headless-twin rejected: { record, errors }[] 객체 배열 * plant "rejected is a list of envelope ids" 문자열 배열 * ``` * * `Array.isArray` 는 통과하므로 그쪽의 새 guard 도 안 걸렸다. id 를 맞추는데 객체라 하나도 안 맞고 * **묶음 전체가 「보냈음」으로 찍혔다.** 422 와 `ok: false` 를 보냈는데 그쪽 행은 재시도 0 · * 오류 없음 · 보냈음이었다. 인티그레이션 레인이 두 파일을 나란히 읽어서 찾았다. * * 우리가 말했는데 상대가 못 알아듣는 모양이면, 말한 것이 아니다. * * ── 왜 배선에서 갈라 놓나 ─────────────────────────────────────────────────── * `reference-hook.ts` 는 엔티티를 물어서 시험 러너가 못 읽는다. 이 판정은 평범한 값으로 확인할 수 * 있어야 하고, 오늘 이 저장소에서 「고쳐도 통과하고 안 고쳐도 통과하는 시험」이 여럿 나왔다. */ /** 훅 응답에 실리는 떨어진 것 하나 — 봉투 id 를 알면 그것을, 모르면 레코드를. */ export type RejectedForCaller = { eventId: string; errors: string[] } | { record: unknown; errors: string[] } /** * 떨어진 것에 봉투 id 를 붙인다. * * **짝은 객체 동일성으로 짓는다.** 유입에 넘긴 레코드가 배치의 원소 그대로이므로(§`takeInSequence`) * 커넥터를 고치지 않고도 봉투로 되짚을 수 있다. 값으로 비교하면 같은 모양의 레코드 둘이 서로의 id 를 * 가져간다 — 그러면 보내는 쪽이 **엉뚱한 행**을 떨어진 것으로 표시한다. * * **id 를 못 찾으면 레코드를 그대로 싣는다.** 빈 id 를 실으면 보내는 쪽이 아무 행도 못 찾으면서 * 「알았다」고 여긴다 — 지금 났던 일이 정확히 그것이다. */ export function rejectedForCaller( rejected: readonly { record: unknown; errors: string[] }[], items: readonly { record?: unknown; eventId?: unknown }[] ): RejectedForCaller[] { const idOf = new Map() for (const it of items) { const id = typeof it?.eventId === 'string' && it.eventId.trim() ? it.eventId : undefined if (id && it?.record !== undefined) idOf.set(it.record, id) } return rejected.map(r => { const eventId = idOf.get(r.record) return eventId ? { eventId, errors: r.errors } : { record: r.record, errors: r.errors } }) }