Master removed the TUI package, the `meta` and `upgrade` subcommands, and
`--config-replace`, and made raw `dsh` require a `--config` overlay. Resolved
onto that shape:
- Dropped this branch's TUI edits with the surface itself, including
`tui.cordis.yml`, `runTui`, and the TUI keyless PTY smoke.
- Dropped the `--config-replace` plumbing rather than reintroducing a flag
master deliberately removed. The gap this branch fixed remains: `dsh -p`
still could not name its composition, so it keeps `--config`.
- Kept this branch's deletion of the personal `$DSH_HOME/config.yaml` layer,
which master still carried, and provided the environment snapshot in the new
raw `runConfig` surface alongside web and headless.
- Ported the headless shutdown PTY test off the personal overlay onto a named
`--config` file, which is what proves that flag now exists on `-p`.
#1381 landed the bar's horizontal position; this branch decides when it is
drawn. The e2e keeps both scenarios and the golden carries both palettes'
pointer-state readings alongside the new edge-offset lines.
Review findings from the first round:
- ui-settings renders its full-viewport panel as a fixed-position DESCENDANT
of the sidebar column, so `pointerleave` never fires when the pointer moves
onto it and the bars stayed drawn after it closed. Leaving is now decided
against the column's box from a document-level pointermove that exists only
while the bars are drawn; the element's own leave stays for the pointer that
leaves the window.
- The rebind gate judges the rule rather than each declaration, so a pair that
mixes `transparent` with an l2 hover no longer passes, and the elevation
half compares whole values instead of token shape.
- Hiding no longer exempts a sheet from the elevated-surface rebind check.
- The e2e polls the reveal before reading a colour for the golden, and pins
that a pointerless scroll draws no thumb.
The session list overflows after a handful of sessions, and its scrollbar
was drawn permanently in a column that is at rest most of the time.
SidebarRoot now tracks the pointer over the whole column and rebinds
ui-theme's scrollbar indirection pair to `transparent` while it is
outside, keeping the thumb for 2s after the pointer leaves so it does not
blink out on the way past. Rebinding colour leaves the list's
`scrollbar-gutter: stable` reservation in force, so revealing the bar
moves no row.
ui-theme's gate now states the widened contract: a rebind targets an -l2
token pair or `transparent`, and nothing else.
$DSH_HOME/.env carried two incompatible jobs. As credentials-local's writable
secret store it could not be hoisted into process.env — hoisting makes every
stored key read as a read-only launch override and blocks rotation from the
TUI and the web page. But its name and dotenv format promise an environment
file, so a DEEPSEEK_BASE_URL sitting beside a working DEEPSEEK_API_KEY in the
same file was silently ignored: only the credential provider read the
document, and it addresses credential references alone.
Split the two jobs into two files.
.credentials.yaml is the provider-managed store: a strict YAML mapping of
CredentialRef to non-empty string, no version field, no wrapper level. Because
it holds credentials and nothing else, a non-mapping root, a non-identifier
key, a non-string value, an empty string, a duplicate key, and malformed YAML
are all rejections rather than skipped entries — loud at boot and at a write,
warn-and-keep-last-good on a live reload. The dotenv physical-line editor
gives way to a patch of the parsed document, so comments and untouched entries
keep their formatting and any string value round-trips, multi-line included.
Writer lock, read-modify-write, atomic 0600 write under a 0700 directory,
watcher, self-write suppression, and quiescent disposal are unchanged.
$DSH_HOME/.env becomes the user's ordinary environment layer. app-boot's new
loadLayeredEnv loads the invoking directory's .env then the Harness home's,
giving user < project < inherited; the home resolves from the inherited
environment first, so a project .env cannot redirect it.
Credential precedence is unchanged: the live environment still wins read-only
over the file, and shadowed writes still reject. Whether a provider-managed
store should instead win over the environment is a separate decision.
No migration: a key already in $DSH_HOME/.env keeps resolving through the new
environment layer, as a read-only env source that shadows the stored one.
Making `.sources` a scroll container turned its `padding-left` from spacing
into a correctness constraint. A scroll container clips inline-start overflow
with no way to scroll it back, and `::marker` is right-aligned to the content
edge, so past nine sources the markers rendered as `0.` and `1.` where `10.`
and `11.` belonged.
`searchMaxResults` is an unbounded positive integer, so size the padding in
`em` against the list's own font to hold a three-digit marker. The browser e2e
measures a `999. ` marker in that inherited font and requires the computed
padding to be at least that wide, pinning the room against the widest marker
rather than one fixture's source count.
PR #1003 re-recorded this golden from a base that predated PR #1150, which
disables the branch control on any message that is not a completed turn's
tail. Both landed on master, whose CI skips the snapshot lane, so the
conflict only surfaces on PRs. The first message's control is now disabled
with its explanatory text.
jsdom resolves no CSS Modules layout and reports scrollHeight === clientHeight
for every element, so the unit test cannot witness the scroll the .sources
max-height produces. Drive it in the real browser instead.
The web-search-round search double now returns 12 provider results with a
title, snippet, and date each. That pins the seam's cap end to end for the
first time in a real composition — searchMaxResults keeps 8, the model-visible
text carries the kept titles and none of the dropped URLs, meta.truncated is
true — and 8 such rows overflow the 320px container. A case after the aria
golden expands the row and asserts 8 <li>, no button, the truncation
indicator, and computed max-height/overflow-y with scrollHeight > clientHeight.
The recorded stream and the aria golden are unchanged: replay is a positional
cursor over the fixture's chunks and the search double is a separate local
endpoint, while the card is collapsed at capture time.