Supply the required directory collaborator to current runtime fixtures, verify absolute result navigation and deployment fallback, and retain the valid V4 working-directory recording instead of the unreleased V3 input that contains a V4-only event.
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.
Review round 1: the settle assertions build their message only when one
is about to fail, so a passing run never pays the host-facts shell probe;
send windows are bounded by event sequence numbers instead of
milliseconds, so two sends that hand over within one millisecond keep
their own events; the replay starts at the send's own input write and
ignores the emulator's terminal-protocol replies; a throwing foreground
inspection is recorded before the send rejects; a sanitizer is attributed
to the most recently spawned terminal, which the class documents as the
sequential-session assumption. A unit spec pins the replay rule against
the session's evidence states, the window boundaries, the reply/input
distinction, and the bounded, escaped rendering. Notes and comments drop
change narration and the unverified claim about the pool's build.
The self-hosted Windows lane settles one or two of seven sends on the
controlled prompt and the rest at the plain silence bound, and the
settle reasons alone cannot say whether the marker was missing, its tail
was invalidated by later output, or the foreground comparison failed.
The case now records every decoded pty chunk with the sanitizer's marker
verdict and prompt tail, every foreground poll, every input write, each
send's settle reason and elapsed time, the session's prompt evidence
replayed from those chunks, and the host, pwsh, PSReadLine, and console
host versions; the settle assertions carry that timeline in their
failure message and onTestFailed prints it when the runner's budget ends
the case first.
promptTailGraceMs returns to the product default: master runs with and
without it took the same 20.8 s, so the lane's degraded sends never held
the state it covers, and a nonzero value there would only lengthen a
never-arriving-tail fallback.
Review round 1: the tolerance now requires the accumulated tail to remain a
prefix of the controlled prompt, so a prompt that arrived and was invalidated by
later output keeps the plain silence bound instead of waiting a tolerance with
nothing left to wait for. The field is validated as zero or at least one
`pollIntervalMs`, matching the handoff grace rule, and tests pin the extended
bound's expiry, the invalidated tail, and that rule. The owning readiness design
note records the new field and Tier 2 bound and cross-links this decision.
Windows sessions on the self-hosted lane settled one or two of seven sends on
the controlled prompt and charged the silence tier for the rest (issue 2487).
The marker is written by the shell's own prompt function and the printable tail
by the same render, so a tail that arrives past `idleSilenceMs +
handoffGraceMs` means a stalled delivery, not an absent prompt.
`dsh-terminal-bash` gains a validated `promptTailGraceMs` bound (default 0,
which reproduces the previous bound exactly). While a send has seen the marker
and its printable tail has not arrived, the silence bound grows by it. The
pwsh Loader-composition case sets 5000 ms, the host class the bound is for, and
still fails when no marker appears at all.
`operations.spec.ts` read the profile's run record from a 100 ms timer armed
before the refused installation whose repair it observes; a loaded runner
reached the repair run after that timer and the case failed with `null`
against the recorded run. The observing child now stays in flight until the
record appears, and the bounded wait starts at the launcher hand-over rather
than at test start.
The loader-composition case counts `stdin_read` settlements but reported only
the count, so its Windows failures could not say which tier settled which
send. The recorded reasons now ride in the assertion message; the threshold
and the assertions are unchanged.
Add a path-check reminder to the bash and pwsh tool descriptions. pwsh
also warns against assigning automatic variables such as $HOME; bash
also asks agents to guard path variables with ${VAR:?}.
- State the contract precisely: the cut never introduces a lone high
surrogate, and an unpaired surrogate the input already carries is not
repaired.
- Document the new export in the package README: a character-budget preview
section naming the persistent bash and pwsh shells and str_replace_editor,
plus a Known Limitations bullet that no longer contradicts it.
- Drop the assertion restatement from the two shell test comments.
- Let the str-replace test helper reuse a root so the clipped context reads
its file inside its own working directory.
Delete constraints the model learns from the call result, shorten
implementation detail, and move per-parameter rules into the owning
parameter descriptions. Bash, pwsh, and fs share one
sandbox_permissions description in dsh-sandbox.
The standard preset's first-turn prompt drops from 7985 to 6551 tokens.
Compaction threshold snapshots lower their context windows by the
1548-token tool-schema estimate reduction so each trigger keeps its
position; their fixtures move to v4 generations.
* feat(approval): localize prompts and allow consent for one turn
* test(approval): update cast baseline and Web PTC prompt pin
* fix(approval): cover PTC language and clarify turn consent
* fix(approval): group turn consent under allow menu
* fix(approval): keep turn consent in memory only
* test(snapshot): refresh owners for merged justification guidance
* fix(approval): drop duplicate allow-once from the allow menu
* refactor(approval): derive turn consent expiry from one check
Replace the turn approval bookkeeping (revocation state, incremental log
cursor, and turn-signal listeners) with one side-effect-free expiry check
over the log and lifetime flags. Events now only withdraw open
presentations early; the expiry check decides whether an answer or turn
consent still applies, including for Sessions without event publication.
* refactor(approval): drop one-turn consent and keep localization only
Remove the allowed-turn answer, the live turn-consent state, the allow
menu, and their tests, notes, and recorded Web scenario. The approval
service returns to the master request path; the request keeps the
localized displayReason for presentation, and the card resolves it in
the active UI language.
* test(snapshots): record the localized justification schema text in new scenarios
---------
Co-authored-by: Turtle <[email protected]>
Co-authored-by: Kaige-Gao <[email protected]>
Conflict resolution:
- docs/config-catalog and docs/module-graph: regenerate with the current
generators, which write the bilingual pages together.
- The conflict came from master's new short-fingerprint pairing records: our
branch still carried the previous full-hash records. Take master's record
format, then re-record the output-retention README pair after regenerating
its two sides.
* feat(i18n): key translation pairing records by heading section
Replace the whole-file blob hashes in *.i18n.yaml with one entry per
heading section that still holds language-specific content, keyed by the
English heading-slug path. Blocks identical across the pair (generated
table rows, code blocks, shared regions) stay out of the hash, so edits to
different sections and identical regenerations merge with Git's default
text merge, including on GitHub.
Remove the dsh-translation-pairing merge driver, its resolver command and
installer wiring; gen-translation-brief recovers last-confirmed text from
commit history. Migrate all active records, archive the superseded Agent
Note, and record the replayed conflict reduction in the new one.
* test(i18n): refresh translation prompt snapshot for the terminology note
* feat(i18n): exclude only code blocks and generated regions from pairing records
Replace byte-equality masking with an explicit rule: a record hash skips
fenced code blocks and BEGIN/END GENERATED regions, the two kinds of
content the pairing gate already requires to be identical on both sides.
Move generated data into regions written into both pages:
gen-config-catalog renders both languages and the record with per-package
regions labelled by code identifiers; gen-module-graph keeps its table in a
region; gen-doc-graphs splices the event matrix regions into the Chinese
page. Add a generic region splice beside the region grammar.
Conflict resolution:
- docs/config-catalog: regenerate the English page; the Chinese page follows the
three shifted Config sources and the bilingual pair record is re-recorded.
- docs/module-graph: the generator rewrites all three artifacts together.
- packages/shell/tool-pwsh-persistent/src/index.ts: keep master's rewrite of the
prompt handling and re-apply the surrogate cap and its import.
- The three package.json dependency entries follow master's workspace:* range
convention, and the lockfile importer specifiers match them.