You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three related type-safety fixes from the code review on PR #52. Grouped
because they all touch the same contract surface
(``TaskDetail`` on both sides of the wire) and share the theme "honest
types at the DDB + API boundary".
## Findings addressed
**#2 — turns_attempted / turns_completed bypass coerceNumericOrNull**
``toTaskDetail`` at ``cdk/src/handlers/shared/types.ts`` was passing
``record.turns_attempted`` and ``record.turns_completed`` through
with only ``?? null`` — same bug class as the
``costUsd.toFixed is not a function`` crash fixed at the fanout
boundary in commit ``9fe704e`` / consolidated in ``c09bfd7``. The
DDB Document-client deserializes ``Number`` attributes as ``string``
on some code paths; any downstream caller doing arithmetic on these
fields would crash at runtime.
Fix extends beyond the two fields the review called out — ALL
numeric fields sourced from the DDB record now route through
``coerceNumericOrNull``: ``duration_s``, ``cost_usd``, ``max_turns``,
``max_budget_usd``, ``turns_attempted``, ``turns_completed``. A new
JSDoc block on ``toTaskDetail`` documents the contract ("all numeric
fields coerced through shared helper; do not bypass") so a future
field addition has a clear pattern to follow. Scope-bounded: the
non-numeric fields (``task_description``, ``pr_url``, …) and
request-body-validated ints (``issue_number``, ``pr_number``) stay
untouched.
**#4 — CLI/CDK type drift on turns_attempted / turns_completed**
Per AGENTS.md the CDK and CLI ``TaskDetail`` declarations must stay
in sync. CDK declared both fields as required (``number | null``);
CLI marked them optional (``number | null | undefined``). The
tightening means the server's guarantee (``toTaskDetail`` always
emits both fields, defaulting to ``null`` when absent on the record)
now flows honestly to the CLI type.
**#10 — channel_source typed as string instead of literal union**
Added an exported ``ChannelSource = 'api' | 'webhook'`` literal union
in both CDK and CLI ``types.ts``. ``TaskRecord.channel_source`` and
``TaskDetail.channel_source`` on both sides now use the narrow type.
Downstream switches/predicates that read ``channel_source`` get
exhaustiveness checking at compile time, matching the internal
``CreateTaskContext.channelSource`` literal already in use at
``create-task-core.ts``. Reviewer's comment: "the internal
CreateTaskContext correctly narrows it but the external types don't"
— now they do.
## Tests
+3 regression tests in
``cdk/test/handlers/shared/error-classifier.test.ts`` under the
``toTaskDetail integration`` block:
- String-typed DDB numeric fields coerce to finite numbers on
the ``TaskDetail`` output (``typeof === 'number'`` + exact value
for all six coerced fields).
- Unparseable numeric strings collapse to ``null`` without crash
(defence test for the non-finite branch in ``coerceNumericOrNull``).
- ``channel_source`` narrows to the literal union — uses
``@ts-expect-error`` to pin that a widened ``'slack'`` fails to
compile, so a future ``ChannelSource`` regression surfaces in CI
immediately.
CLI test fixtures updated to include ``channel_source: 'api'`` and
``turns_attempted: null`` / ``turns_completed: null`` where the
non-optional fields were previously omitted. No CLI test count
change — the fixture additions satisfy the stricter contract
without requiring new test bodies.
CDK suite: 1032 passing (was 1029). CLI suite: 190 passing.
Refs: krokoko code review on PR #52 (findings 2, 4, 10)
0 commit comments