- timeZoneError and browserTimeZone switch over TimeZoneRejection instead of
a ternary: each reason that shares a diagnostic is its own case, and the
default arm assigns the reason to never so adding a reason fails the build
(assertNever where the package already imports it). The unsupported cause
is still forwarded.
- the request-zone suite now exercises the blank refusal, so every case the
switch lists is covered rather than sharing one ternary branch.
- Duration documents that a span is signed: a negative count builds and
decompose() refuses it, so the caller states its own sign policy.
The package API carried members and result types ahead of any caller. The
span now publishes fromMilliseconds and decompose, the two entries
dsh-time-context calls; the seconds constructor, the zero identity, the
arithmetic and ordering operations, the millisecond getter, and the four
fractional conversions land with the caller that needs them, recorded in
the Agent Note and the README limitation.
The package entry names its exports instead of re-exporting both modules,
so the three result types and DurationParts, which no consumer names, are
no longer part of the published surface. Every remaining new name has a
named consumer: schedule imports the nine parser and bound names plus
four rejection types, and canonicalizeTimeZone has seven consumer files.
origin/master removed the runtime invariant plugins and the package rule
that asked a README to justify the absence, so the text that cited that
mechanism no longer refers to anything:
- the Agent Note states the elapsed-rendering change without the reading
invariant it used to cite
- the same note no longer says the durable reading format is one an
invariant validates
- the util/time README no longer explains why no invariant companion is
published, and its module JSDoc no longer claims the library formats
nothing while listing a canonical instant renderer
- plus and minus construct through fromMilliseconds, so the documented
bound has one enforcement point; the negative control reddens the new
assertion
- the class and README say Duration overrides no coercion hook rather
than having none, and decompose says which parts are floored
- the note no longer calls the differential a review probe, records that
Schedule imports the bound constants, that both strict parsers refuse
year 0, that Schedule's suites do assert a few message substrings, and
that the util charter bans harness dependencies rather than external
packages
- Duration rejects a count outside the exactly representable millisecond
range, which is what makes decompose's whole-part identity exact; the
postcondition is asserted over a sweep and a random sample
- the to* JSDoc no longer claims a round trip restores the count: each is
one division, so it answers the nearest double
- a test rules out a Symbol.toPrimitive coercion hook, the README names
both implementation modules, and the note records that the value type
ships its full operation set before its second consumer exists
Duration holds one finite millisecond count behind a frozen class: a
class rather than a branded number because TypeScript erases a brand
through arithmetic, so only an instance can stop seconds and
milliseconds from being added. It adds, subtracts, orders, converts to
larger units fractionally, and splits a non-negative span into whole
days, hours, minutes, and seconds. There is no valueOf and no toString,
so a reader always states its unit.
dsh-time-context reads its elapsed line through it and keeps the
wording: a backward clock reading still clamps to zero there. The
rendered text is byte-identical for every finite sample, verified by a
differential over more than eight thousand inputs. A non-finite sample
now throws from the span constructor instead of rendering 'NaNs', which
the package's own reading invariant already refused.
- the constants name one bound each
- resolveLocalInstant reuses an instance a caller already holds instead of
copying it per candidate date
- the README entry-point paragraph drops its duplicate sentence
- the note replaces an invalid reason for the zone mapper and a
review-vantage sentence with the facts
- isFourDigitYearInstant is the shared predicate; Schedule's five
representability guards delegate to it instead of re-deriving the range
- the reason for a field outside its calendar range is out_of_range in
both strict parsers, so year 0 no longer reads as a wall-clock problem
- LocalCalendarTime.year states which entries reject year 0 and which
accept it at the UTC edges
- the note counts the five removed expressions and records that the zone
mapper is a silent catch-all; the Chinese side mirrors the English
paragraph order
- the README no longer claims resolveLocalInstant answers a string
- the accepted-input table records the widened plain date-time input
- canonicalClientTimeZone is named as the wire-boundary form rather than
left outside a blanket claim about discriminated results
- the non-canonical scheduledAt diagnostic has one source
- resolveLocalInstant accepts an already-built plain date-time, so the
recurrence walkers drop their private copy of the disambiguation and
round-trip decision
- decodeInstant narrows its field explicitly and drops the assertion
- the model-input exhaustiveness fallbacks throw a plain Error, matching
the timing-change fallback, while durable decoding keeps
ScheduleLogError
- the library module and README state which entries answer a
discriminated result
time-context kept a third copy of the IANA profile: its own regular
expression and its own Intl canonicalization in request-zone.ts. It now
reads a request's reported zone through canonicalizeTimeZone and keeps
its stricter policy — an alias whose canonical answer differs from the
reported value is still refused, with the same three diagnostics.
The shared rejection now carries the platform error it caught, so the
unsupported refusal keeps its cause in both time-context and Schedule.
- Instant entries that take already-validated values throw on violated
preconditions; the module header said every entry answers a result.
- formatFourDigitYearInstant also refuses a non-integral epoch.
- Schedule's reason mappers switch over the closed unions with a never
default, so a new library reason fails the build instead of falling
through to a catch-all.
- The Agent Note claimed the schedule suites pin these diagnostics; they
assert codes only, and the note now records the added message assertion.
Move Schedule's strict RFC 3339 instant, local-calendar, and IANA zone
parsing into @deepseek-ai/dsh-util-time, and have Schedule consume it.
The shared entries answer discriminated results and own no failure
vocabulary; Schedule keeps its error codes, diagnostics, durable record
shapes, and all recurrence arithmetic.
- protocol: isDSHRemoteGatewayError -> isDSHRemoteError (the class is
protocol-wide, not Gateway-specific); remoteErrorOf drops its
instanceof Error precondition and tests the marker plus a string code
structurally, so a marked failure from another realm no longer reads
as a local defect.
- session-controller: the live-follow packed-record protocol violation
now throws a marked RemoteError('gateway/internal'), landing the
session in openState=error instead of an unhandled rejection;
pinned at the transport and session levels.
- util-time: correct the invariant companion's @module name.
- regenerate the tool-cordis catalog for the marker rename.
- classify a carrier throw under a caller-aborted signal as
gateway/cancelled instead of gateway/internal, matching the code the
Host produces when the abort wins the wire round-trip.
- read $host facts from the construction-time Connection handle,
matching $stream; the service cannot be replaced without restarting
this plugin, so the live re-lookup was dead complexity.
- state rebuiltFailure's actual contract in its comment: codes pass
through verbatim without runtime validation.
- correct the @module name in dsh-util-time.
Single RemoteError with a merge-extensible, domain-prefixed code map;
owners throw at the failure point; streams surface marked failures;
clients consume ctx.remote directly with isRemoteFailure as the only
discrimination point and construct no failure instances.