import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql' import { ScalarObject } from '@things-factory/shell' import { backfillPeriodFacts } from './backfill-period-facts.js' /* * 지난 기록 채우기의 **문** — 커넥터가 트윈이 없던 동안의 날들을 뒤늦게 넣는다. * * ── 왜 문을 따로 두나 (2026-08-30) ──────────────────────────────────────── * 라이브 유입과 판단이 다르다. 라이브는 트윈의 「지금」을 바꾸고, 이쪽은 저널에만 쓴다. 같은 문으로 * 받으면 옛 표본이 가드에 막히거나 트윈의 시계가 과거로 끌린다(§`backfill-period-facts`). * * 레코드의 **모양은 라이브와 같다** — 커넥터가 다른 어휘를 배울 필요가 없다. 다른 것은 받는 종류뿐이고, * 그 판단은 커널의 유입과 `judgeBackfill` 이 한다. * * ── 왜 이 파일이 늦게 생겼나 ────────────────────────────────────────────── * 채우기 자체는 만들어 두고 **부르는 곳을 만들지 않아** 쓸 수 없는 채로 있었다. 8월 29일 발전량을 * 되살리려다 드러났다. 같은 부류를 그날 두 번 겪었다(커널의 발전 기간 문도 없었다). */ @Resolver() export class TwinBackfillResolver { /* 트윈의 모든 변경과 같은 게이트다(ADR-0027) — 저널에 사실을 적는 일이므로 변경이다. */ @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)') @Mutation(returns => ScalarObject, { description: 'Backfill closed-period facts into a twin journal without touching its live state. Records use the same shape as live ingest; only closed-period facts are accepted (usage/generation periods, generation price, tariff basis, bill). Point-in-time observations are refused with a reason, as are times beyond journal retention. Re-running is safe: a period already present is skipped, because a closed period is identified by its content. Returns { written, skipped, refused }.' }) async backfillTwinPeriodFacts( @Arg('instanceId') instanceId: string, @Arg('records', type => [ScalarObject]) records: unknown[], @Ctx() context: ResolverContext ): Promise<{ written: number; skipped: number; refused: { record: unknown; reason: string }[] }> { return await backfillPeriodFacts({ domainId: context.state.domain.id, instanceId, records }) } }