Skip to content

refactor(render-payload): migrate five payload lanes to Outcome, one boundary log site (2l6.1)#104

Merged
brandon-fryslie merged 1 commit into
mainfrom
brandon-git-segment-2l6.1_outcome-lanes
Jun 12, 2026
Merged

refactor(render-payload): migrate five payload lanes to Outcome, one boundary log site (2l6.1)#104
brandon-fryslie merged 1 commit into
mainfrom
brandon-git-segment-2l6.1_outcome-lanes

Conversation

@brandon-fryslie

Copy link
Copy Markdown
Contributor

Closes brandon-git-segment-2l6.1 (follow-up surfaced by PR #96).

What

buildRenderPayload had two failure-visibility contracts side by side: the git/cache lanes carried typed Outcomes with boundary logging, while session/today/context/metrics/tmux still .catch(() => null) and relied on interior debug() logging — two types for one behavior at one boundary ([LAW:one-type-per-behavior]).

All seven lanes now carry Outcome(ok | absent | failed); the boundary is the single log site ([LAW:single-enforcer], [LAW:no-silent-failure]); the doc comment collapsed to one sentence as the ticket specified.

Per-lane classification (where the semantics are known)

  • sessiongetSessionUsageFromPath returns Outcome; failed parses are NOT cached in the store (only ok records enter the map — same rule as the git cache), so the next render retries.
  • todayTodayInfo tightened to non-null totals ([LAW:types-are-the-program]); "no usage today" is absent; a failed seed or failed active-session ingest fails the projection loudly instead of summing a confidently-wrong total. The once-a-day seed logs its own per-session parse failures (it is its own effect edge — no render boundary to carry outcomes to).
  • context — unreadable transcript is failed; transcript with no usable usage entry is absent.
  • metrics — hook without a cost block is absent; MetricsInfo tightened so only lastResponseTime stays nullable (the one field whose domain genuinely has no answer sometimes).
  • tmux — module cache stores the resolved Outcome; a spawn failure stays durable (no re-spawn per render) but flows to the boundary as failed instead of being demoted to null.

The deeper seam

parseJsonlFile swallowed all read errors to [] — with that in place the new failed arms were unreachable for real read errors. Now ENOENT stays [] (a new session pre-first-write is the domain's genuine "no entries"), and every other error (EACCES, EIO) propagates so the consuming provider classifies it. All three callers are the providers migrated here.

Boundary cleanup

One generic lane() helper replaces the per-lane null plumbing — the five nullP<Awaited<ReturnType<...>>>() incantations and both as Promise<Outcome<...>> casts disappeared because the types converged. One pure take() fold projects single-value lanes; the log effect happens once at the edge ([LAW:effects-at-boundaries]).

Verification

  • pnpm typecheck, pnpm lint, pnpm check:protocol, pnpm build all clean
  • Full suite 1133/1133; new boundary tests pin the unified contract (failed lanes log exactly once + project missing, absent lanes silent, thrown stubs totalized to failed with lane-name prefix)
  • Provider tests updated to the new contract: unreadable transcript → failed (chmod 000), missing transcript → graceful zero-entry, no-cost hook → absent

Note

The z.ai reviewer account is 429-blocked (account-level, restoration requested) — the Code Review check will fail until restored; this is expected and not caused by this PR.

…boundary log site (2l6.1)

Every provider lane (git/cache/session/today/context/metrics/tmux) now
carries Outcome(ok|absent|failed); buildRenderPayload is the single log
site [LAW:one-type-per-behavior][LAW:single-enforcer][LAW:no-silent-failure].

- session: getSessionUsageFromPath returns Outcome; toSessionInfo takes
  non-null usage; store ingest/aggregate carry failure, failed parses are
  not cached (only ok records enter the map, same rule as the git cache)
- today: TodayInfo tightened to non-null totals; "no usage today" is
  absent; a failed seed or active-session ingest fails the projection
  loudly instead of summing a wrong total; seed logs its own per-session
  parse failures (its own effect edge)
- context: unreadable transcript is failed, no-usable-entry is absent
- metrics: no-cost hook is absent; MetricsInfo tightened (only
  lastResponseTime stays nullable)
- tmux: cache stores the resolved Outcome; spawn failure stays durable
  but visible as failed at the boundary instead of demoted to null
- parseJsonlFile: ENOENT stays [] (new session pre-first-write); every
  other read error propagates so providers classify it as failed — the
  old catch-all-to-[] dressed EACCES/EIO as an empty session
- boundary: one generic lane() helper replaces the per-lane null
  plumbing and casts; one pure take() fold; doc comment collapses to
  one sentence

@brandon-fryslie brandon-fryslie left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial review

This is a well-executed refactor that correctly unifies seven provider lanes from two competing failure-visibility contracts into one Outcome-typed boundary. The lane/take helpers are correctly implemented, exception safety via .catch is preserved for all async paths, TodayInfo/MetricsInfo type tightening is correctly reflected at every usage site, and the parseJsonlFile ENOENT/non-ENOENT split is semantically sound. The test suite is thorough and the new boundary tests pin the unified contract precisely. No correctness defects found.

@brandon-fryslie brandon-fryslie merged commit 2a5cc49 into main Jun 12, 2026
6 of 7 checks passed
@brandon-fryslie brandon-fryslie deleted the brandon-git-segment-2l6.1_outcome-lanes branch June 12, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants