Awesome Reviewers

Treat null/undefined/omitted values as distinct states and make that distinction explicit in both types and request/transition logic.

Apply these rules: 1) Match runtime normalization with types

2) Encode “present only in some phase” in TypeScript

3) Don’t silently fall back on missing required context

4) Make reset intent explicit in API payloads

Example pattern (normalize + type-gate + explicit reset):

// 1) Presence is phase-gated: invalid combinations are unrepresentable
type BeforeRun = { kind: 'beforeRun'; entry: 'run' | 'continue'; snapshot: Snap };
type AfterRun = { kind: 'afterRun'; snapshot: Snap };

type Ctx = BeforeRun | AfterRun;

function run(ctx: Ctx) {
  // OK: only exists for beforeRun
  if (ctx.kind === 'beforeRun') {
    handleEntry(ctx.entry);
  }
}

// 2) Normalize null vs undefined at the boundary
type Patch = { baseUrl: string | null | undefined };
function toConnectionPatch(p: Patch) {
  if (p.baseUrl === undefined) return undefined;      // omit: no change
  return { baseUrl: p.baseUrl };                     // null => explicit reset
}

// 3) Avoid silent fallback
function resolveMode(requested: 'cloud' | 'local', repoUrl?: string) {
  if (requested === 'cloud' && !repoUrl) throw new Error('repoUrl required for cloud');
  return requested;
}

Following this consistently prevents stale state, accidental “empty means nothing” bugs, and incorrect defaults caused by ambiguous null/undefined handling.