The effort field's existence check was unreachable — EFFORT_FIELD is
total over the two known families — and a coverage exemption was papering
over the branch, which the merged toolchain no longer honored. Taking the
narrowed family as a parameter makes the lookup total at the type level,
so the check and its exemption both disappear. The rendered output is
unchanged: the browser goldens replay byte-identical.
Drop the explicit-undefined base fixture exactOptionalPropertyTypes
forbids (the repository trusts TypeScript at typed same-process seams —
no test for an input the static interface excludes; coverage holds), and
reshape the async-listener containment fixture as an unknown-returning
function: the earlier inline cast was silently stripped by the staged
oxlint fixer, leaving a shape the next lint pass rejects.
The seam README states the JSON-shaped write boundary, watch-disposer
quiescence, async listener containment, and the drained teardown; the
provider README rewrites Behavior around the operation chain,
read-modify-write, writer lock, ready reconcile, and leaf-level YAML
diffs, and updates Known Limitations to the residual guarantees.
A new Agent Note records the round's decisions and supersedes the
original note's deferred-lockfile alternative (cross-linked in place).
Chinese counterparts updated pair-by-pair (three briefed minimal
updates, one whole-document translation); type-equiv, config, cordis,
and module-graph catalogs re-recorded.
Review round three found the provider's write path could destroy state it
never observed:
- Watcher reloads and document writes ran on two independent promise
chains, and a write rendered the whole next document from the cached
text. An external edit still inside the debounce window (or missed
outright) was overwritten, and the follow-up reload no-oped because the
post-rename content matched the cache — the edit vanished without a
trace. Reloads and writes now share one operation chain, and every write
starts by reconciling the on-disk text into the seam before rendering,
so unobserved sibling sections survive and publish first. An unparsable
on-disk document fails the write loud instead of being overwritten.
- The initial load raced the watcher's own setup: a change written between
that read and the watcher becoming active never fired an event. The
watcher's ready signal now queues one reconcile, closing the gap.
- Two processes sharing a harness home rendered from independent caches,
last writer winning. Writes now hold a wx-created <file>.lock sibling
around the read-render-rename cycle with bounded backoff, a crashed-
holder stale takeover, and a deadline failure; readers stay lock-free
because the rename commit is atomic.
- renderYaml replaced the whole namespace node, dropping every comment
inside the section. The next section now lands as a leaf-level diff
(set changed values, delete removed keys), so comments, anchors, and
formatting survive on every untouched node and on the key of every
changed pair; arrays still replace wholesale when unequal.
Review round three found four seam defects:
- A watch() disposer only removed the observer from the set; an invocation
already chained onto the watcher tail still ran after disposal. Watchers
now carry an active flag checked when a queued invocation would start,
and the service dispose drain awaits started invocations (pendingTails)
beside the write queues, so disposal is quiescent.
- The settings/updated manual fan-out caught only synchronous throws; an
async listener rejection escaped as an unhandled rejection. Thenable
returns are now contained through the shared listener diagnostic, and
the event contract documents that the INVARIANT rethrow serves
synchronous listeners only.
- structuredClone admitted Dates, Maps, BigInts, and cycles that YAML/JSON
storage silently distorts on reload (a Date lands as a timestamp string,
a Map as a plain map, a BigInt as a number). The write snapshot is now a
single-pass cloneJsonShaped walk that rejects non-JSON values with their
path before anything persists.
- mergeLayers' per-entry undefined guard became dead code once the clone
strips undefined entries at the boundary; removed, with the sparse-patch
contract restated at its enforcement point.
Both files fit their ceilings on each parent; the merge union of this
branch's settings rows with master's typert row and source-launch rewrite
overflows by 6 and 2 words. Every added row is a fixed-format layout or
package-table entry with nothing to relocate, so the ceilings move to the
union size.
The Models page drops the generic schema renderer and the visible
environment-variable field: each editor is a curated per-family card whose
primary input is one write-only API key stored under a derived
<ROUTE>_API_KEY reference (recorded as apiKeyEnv in the pi-ai profile), an
unkeyed whole-section provider opens as its setup card, and the collapsed
customized-settings fold carries baseURL/reasoningEffort (deepseek) or
reasoning (pi-ai). dsh-client-schema-form reduces to the schema/draft model
layer (no React).
The seat's height publisher needs a ResizeObserver stub in every spec that
renders ConversationRoot (jsdom has none), and the two branch-tail StatsLine
assertions move to the grouped detail-row output.
Stats line moves into the InputBar's new footer slot (sharing the card's
width column) and expands to the design's grouped detail row: turns/steps,
LLM and tool wall time, cache hit, and input/output token split, all
derived client-side from the snapshot. The composer stack owns one 8px
rhythm, the seat fades the transcript through a fixed 36px gradient band,
back-to-bottom follows a live --dsh-composer-height, and goal/todo strips
share one 752px tip-fill column.
apps/cli/cordis.yml gains settings-local, credentials-local, and the bare
dormant llm-pi-ai row (manifest deps added for the resolver contract);
llm-deepseek drops its !!js apiKey inline for per-request credential
resolution. Both adapters tag apiKeyEnv role('credential-ref') so the
form mounts the credential control. The web e2e scaffold isolates a
harness home per run — an in-process boot must never touch the
developer's real ~/.dsh — and the new models-settings scenario pins the
whole loop through the shipped app: dormant directory as add vocabulary,
schema-driven editor apply landing in settings.yaml, the route
registering live (topology frame), and a write-only key landing in the
temp .env with the configured badge converging. A hermetic test-owned
reference name keeps a developer's real provider keys from flipping the
badge. schema-form joins the platform module table (seed + externals)
so client bundles share one instance.