Review follow-up: name the parity job in the workflow header, point the unit
comment at ci.yml's Linux jobs instead of a nonexistent `checks` job, state the
budget as a completion ceiling and raise it to 60 minutes so the slowest observed
runner keeps headroom, and let the owning Agent Note describe the budget and the
workflow's statuses without restating numbers the workflow owns.
The seatbelt leg of the sandbox-e2e matrix also ran `pnpm run test`, and the
complete unit suite takes 16 to well over 20 minutes on macos-latest, so the
shared 20-minute job budget cancelled the leg on most master pushes — before
the Seatbelt confinement e2e ever ran. Move the parity run into a `unit-darwin`
job with a 45-minute budget; the four e2e legs keep 20 minutes.
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.
Review found two factual errors in the prose this change adds.
plugin-install-cancel: the scenario pauses the page clock before the
enable click, so the highlight's `setTimeout(clearHighlight, 2400)`
cannot fire while the status poll runs. The comment claimed a reload
could outlive that expiry, and the note explained the two CI timeouts
at the untouched highlight locator the same way. The cause of those
timeouts is not diagnosed, so both now say so instead of offering a
mechanism the paused clock rules out. The pre-change file reproduces the
same hang under the same lane command, which the note records.
workspace-new-session-folding: `nth(7)` is the first row the batch
reveals, not the last established one, and the reflowing `showMore.click()`
is above the comment, not below. Both are corrected, and the workspace-row
comment names what `:hover` reveals instead of restating a cell swap the
project row does not have.
turn-tail-actions: clear the pointer modality flag with `Shift`, which
moves no focus, matching focus-rings.e2e.ts.
The zh dictionary rendered subagent, list_agents, interrupt_agent, and
ralph tool cards with 代理 (proxy) for Agent. Use 智能体 like the goal hint
and wait_agent copy already do.
The self-hosted Windows serial lane failed 12 cases from one cause: the host
exported Git's indexed command-line configuration (`GIT_CONFIG_COUNT` with
`GIT_CONFIG_KEY_0` / `GIT_CONFIG_VALUE_0`), and the shared subprocess scrub
removes credential-shaped names, so a child inherited the counter without its
key and died with `error: missing config key GIT_CONFIG_KEY_0` before it read
any configuration file.
Both spec files now delete that group from their own environment before they
spawn Git, because they judge profile behavior rather than host state. The
product-side handling stays out of this change; it is preserved on the branch
`deferred/git-command-line-config-scrub-20260927` for a later product PR.
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.bare GIT_CONFIG_VALUE_0=true pnpm exec
vitest run packages/boot/plugin-manager/tests/github-connection.spec.ts
packages/boot/plugin-manager/tests/manager.spec.ts -> 12 failed at the parent
revision, 95/95 passed with the isolation.
The three-file lane run passes all three scenarios in three consecutive
local runs, and the install-cancel scenario keeps a residual exposure:
it finds the card through the highlight, so a reload that outlives the
highlight while the card is absent fails that locator. One lane run
failed there and it did not reproduce.
Master's 07dcdb46e5 moved the question step's settled title into the
`Ask question waiting` row of mid-steer.expected.md, so the button this
scenario waited for no longer renders. The wait therefore never resolved and
parked the scenario on the question page, producing the lane's cascade: `no
turn/end within 30000ms`, no live root session matching session.v4.jsonl, and
`llm-replay: fixture not fully consumed`. `captureStableAria` already polls
until two consecutive normalized captures agree, so the settled state is
captured without the extra wait.
DSH_SNAPSHOT=replay pnpm exec vitest run --config vitest.web.config.ts
apps/web/tests/steering.e2e.ts -> 7 passed (reproduced twice); with the wait
present the same command fails at its 10 s bound and again widened to 30 s.
Three scenarios captured an aria region while the state their golden
records was implied by the pointer, the keyboard, or an in-flight Host
read, so the capture could land on the other state.
workspace-new-session-folding captures two goldens of one sidebar, each
recording a hovered row that Rows.module.css renders with the row
actions in place of the relative time. Both hovered rows are now
established before their capture instead of inherited from wherever the
Show-more click left a stationary pointer. The serial self-hosted master
lane failed this scenario in every failing run observed between
2026-09-24 and 2026-09-27.
turn-tail-actions records a tooltip beside the focused Copy footer in
running.expected.md. Tooltip ignores focus while the last input came
from a pointer and only a keydown clears that flag, so the scenario now
establishes keyboard-owned focus and waits for the bubble.
plugin-install-cancel polls the enabled card's status instead of
sampling it once. The store marks the card and starts the directory
reload the Host answers, and the status flipped disabled -> running 61 ms
after the highlight in three local runs, which the single read sampled
inside.
Goldens, product code, and assertion strength are unchanged.
`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.
Closing the provider editor confirms its write, but the browser directory
refresh lands after it, and the reopened editor copies the previous input
selection into a new draft — checkbox polling cannot correct a draft that
was already built. Run 36159502081 (job 108152262196) read a stale `true`
from that draft at models-settings.e2e.ts:395.
Wait for the saved-provider notice, which the refresh publishes, before the
third reopen. The adjacent reopen in the same test file waits on the same
notice.
The browser lane pins the browser to Asia/Shanghai, and formatMessageClock
prefixes clock.md / clock.ymd to the clock once a message's local calendar
day differs from the renderer's. Four scenarios left both clocks on the
wall clock while their message times are stamped during the run — a live
composer send by the Host, a `createdAt: 0` seed anchored from Date.now()
— so the job that began six minutes before local midnight rendered
`9/25 23:58` where their goldens record `{{clock}}` (run 36157253018, job
108144715986: steering, markdown-images, message-actions, and
reference-composer failed together, then passed in the next run).
Read WEB_FIXTURE_TIME on both clocks instead. Live-stamped steering suites
install pinHostClock(), a Date.now spy that advances from the anchor with
real elapsed time; pinBrowserClock() re-applies one frozen instant from the
anchor on an interval, because holding a single instant removes elapsed
time from the 150 ms process-title hold and the composer's 10 ms
composition guard, which then never elapse. Seeded scenarios anchor their
seed beside the pinned browser clock, which keeps the relative-age buckets
their goldens assert.
The steering mid capture waits for the settled question title its golden
records, and the whole-queue scenario waits for the running turn before its
queue gestures, whose decision resolves against an agent that reports busy.
Goldens, the Asia/Shanghai browser timezone, and product rendering are
unchanged.
Reading Git's path text verbatim to keep trailing whitespace duplicated the
line-terminator helper that scripts/change-scope.ts already owns, which the
duplication gate rejects. Canonicalizing the fixture-side common directory
with realpathSync.native removes the text parsing entirely: short names,
junctions, and directory symlinks all collapse onto the physical directory
the installer resolves, and the injected path stays exact.
Drops the trailing-space case: with no text trimming left in the
derivation, that path already matched the installer before this change, so
the case could not fail and proved nothing.
git() trims its output, which also drops a worktree directory's own
trailing whitespace; the installer removes only the line terminator. Derive
the expected lock path through gitPath()/stripGitLineTerminator(), which
applies the installer's exact rule, so the same-string claim holds for
every path.
Adds the trailing-space injection case (skipped on Windows, which cannot
create such a directory); it fails with the same probe ENOENT under the
trimming derivation.
The five lock-access cases only diverged on a host whose temp directory
already carried a short name or directory alias. Route one case through an
aliased worktree root (a junction on Windows, a directory symlink
elsewhere) so the installer's resolved common directory necessarily differs
from the path this spec composes; the case fails with the same probe ENOENT
under the previous derivation on every platform.
The alias case shares the injection body through
expectInjectedLockAccessFailure, so the five existing case names and their
assertions are unchanged.
The lock-access cases injected a filesystem failure on a path this spec
predicted from the mkdtemp fixture directory. The installer resolves its
common directory against Git's own top-level path instead, and the host
spells that path differently: Git canonicalizes Windows 8.3 short names
and the macOS /var symlink. When the spellings diverged the injection
never fired, so the probe file was never written and all five cases
failed with ENOENT at the probe read - the signature seen in the Windows
coverage lane.
Resolving the relative common directory against Git's top-level, as the
installer does, makes the expected path identical to the one the
installer touches on every platform. The five cases fail deterministically
on macOS before this change and pass after it.
`ToolCallRecovery.observe` compared `entry?.turn` against the event's turn
before reading `entry.step`. For a `tool/result` whose callId never registered
as pending and whose event carries no turn/step, both sides were `undefined`,
the guard passed, and the `entry.step` read threw. A pending entry is now
required before the turn and step comparison.
The probe needs a value that cannot be inferred from the prompt, not protected information. Describing that generated UUID as a private secret made the API test depend on an unrelated disclosure decision. Use an explicitly synthetic sample label and a benign formatting update while preserving execution audits, first-continuation cache assertions, and disabled retries.
Open the running Think disclosure explicitly when a process group also contains tool updates, and keep the fork process summary expandable in its ARIA expectation. Record the Goal request-series marker in a V4 fixture successor while preserving the historical V3 recording. Production behavior and the scroll assertions stay unchanged.
Record explicit request-series starts and the tool additions admitted when resuming a seeded parent with no tool declarations. Keep tool names and the shifted source-event reference alongside that activation history. Refresh only the affected owner-local outputs to the current writer format; retain historical replay fixtures and the unchanged child expectation.
Tool changes previously forced prompt consolidation even when the prepared
route could activate new tools through history. Rewriting the first system
message, or clearing an earlier appended prompt, reset the declaration
series and turned additions into immediate declarations.
Only routes without toolUpdate need that tool-triggered consolidation.
Capable routes append the full changed prompt and keep existing messages.
Explicit series starts, surface replacements, and unsupported prompt
routes still consolidate; retained-name definition changes still rebuild
tool declarations in Session.
Cover both update modes and reset paths, and pin the combined prompt/tool
transition through the shipped headless profile with shared schema goldens.
Run a real DeepSeek conversation through the built SDK profile and change the scoped tool registry through a model-invoked controller. Compare the first post-addition cache usage with the preceding request after unique user history has been cached; verify deferred declarations, chronological activation, and actual tool execution. Removal remains covered without requiring its cache prefix to survive. Disable test and provider retries so a warmed transition cannot hide a cache regression.
Interpolating an empty body directly produces empty parentheses in the failure title, making the empty-response case harder to distinguish from the truncated JSON case in CI logs.
Use JSON formatting for the existing case object so the operation and body are visible, including an explicit empty string. Keep the same inputs, error classification, original-cause assertions, and single-request checks.
The attachment-policy fixtures introduced three assertions to unknown and
changed two previously recorded assertions. The new repository gate rejects
those escape hatches even though the earlier branch CI passed.
Give the user, tool-result, and inbox fixtures their required V4 message
metadata and declared identifier types. Declare the fixture's plugin content
tag through ContentBlockMap, and check the flat tool-result fixture directly
as its concrete SessionEvent type. Keep intentional malformed inbox entries
and opaque extension fields in the existing negative cases.
Remove all five unknown bridges without adding allowances. Delete the two
baseline entries retired by the rewritten existing fixtures; the other
three assertions were never part of the accepted baseline. Attachment
runtime code and the tests' access-denial and complete-log assertions remain
unchanged.
Validation: the Host TypeScript aggregate and all 52 archive tests pass.
verify-no-unknown-casts reports no new or stale entries, with 1575 existing
assertions remaining; the baseline adds zero allowances and retires two.
The operation, HTTP status, and parser cause added by responseJson belong to JSON decoding failures. Existing metadata validation also uses INVALID_RESPONSE but does not provide those fields, so the broader phrase parsing failures promises too much.
Limit the README guarantee to JSON decoding and keep metadata-validation behavior unchanged. Keep the Agent Note focused on why the existing INVALID_RESPONSE code is used without inferring a transport retry, and link to the README for the error-field details. Update both languages and their pairing records.
Attachment reads and ZIP export previously inferred ownership from common
field names on arbitrary events. Opaque ignorable plugin payloads could
therefore cause storage reads, while declared compaction content was missed.
Select attachment-bearing fields by current built-in event type in both
readers. Read direct image and file blocks, including flat V4 tool results
and developer messages, without interpreting extra block fields. Leave
unknown payloads and malformed inbox entries opaque. The archive preserves
the complete logical log even when its payload does not authorize a read.
V4 validates title requests as one text block, so do not treat them as an
attachment carrier. Compaction keeps its declared ContentBlock fields for
valid custom producers; the basic producer's text-only output is documented.
Historical wrappers remain the adjacent converters' responsibility rather
than a second compatibility path in attachment readers.
Adapt the original policy change to master after V4 landed, retaining its
author credit and scope. Cover tool-result export explicitly, keep Team
fixtures free of invented message roles, and verify that denied carriers
never read storage. The 889 owning tests pass with 100% statement, branch,
function, and line coverage for both changed runtime files.
Co-authored-by: Tianyi Cui <[email protected]>
The new no-unknown-casts check rejects two assertions introduced by these projection regressions. Both fixtures intentionally represent unknown content: malformed input sent to the admission checks, and a cached plugin block whose opaque field was already lost.
Declare those inputs as unknown variables instead of asserting their expressions to unknown. Keep the same malformed values, content assertions, checkpoint replay, and production validation. Do not expand the recorded cast allowances or change any runtime behavior.
The cold checkpoint regression calls only the projection registry register and restore methods. Neither needs SessionStore, so mounting and disposing that extra service obscured the fixture dependency. Keep the registry lifecycle and the recovered-content assertion unchanged.
The Team projector declares only a subset of current content validators; the existing file and image validator gaps are not fixed by opaque JSON preservation. Limit the new prose to those local validators instead of promising full ContentBlockMap validation.
Keep cache behavior in the README and invalidation rationale in the existing decision note. Document why unknown JSON objects pass through by reference without changing any parser branch or expanding this repair into unrelated validation policy.
A valid unknown content block can own a __proto__ JSON field. Zod object reconstruction silently drops that key when Team events are projected and when checkpoints are admitted, even though the original Session log still contains it.
Validate the unknown block tag without reconstructing its decoded JSON fields. Keep the current V4 local validators and the rejection of retired tool-result content. Bump only the Team projection cache stateVersion from 3 to 4 so old lossy checkpoints are rebuilt from the unchanged log; this is not a Session format or schema change.
Cover direct and nested opaque JSON fields inside accepted native V4 content, plus public registry restoration from an older lossy checkpoint. Historical tool-result conversion remains owned by the Session migration and is not restored in the Team parser. Update the README and existing Team decision rationale together.
Adapted from the Team JSON preservation work in b8f1da92599123e5d5bae6c174f73b5b778f3554 by Tianyi Cui.
Co-authored-by: Tianyi Cui <[email protected]>
Empty or truncated successful Files API bodies currently escape as bare SyntaxError values. They omit which Files operation failed and the successful HTTP status, obscuring the diagnostic while the caller still needs to reject the response.
Route upload, list, retrieve, and delete JSON parsing through one helper that converts only SyntaxError to LlmError INVALID_RESPONSE. Preserve the operation, HTTP status, and original parser cause; transport and cancellation failures retain their original identity. Do not introduce retries or reinterpret malformed JSON as a successful response.
Keep regressions for both Messages and Chat Completions, all four operations, empty/truncated bodies, single request execution, and non-parser error identity. This improves classification and diagnostics without claiming to repair the remote cause of intermittent empty responses.
Co-authored-by: Tianyi Cui <[email protected]>
The merged concurrent migration test used an as-unknown widening assertion after JSON.parse. Master now rejects newly introduced unknown casts, so that test blocks the repository gate and unrelated PR validation. Give the map callback an explicit unknown return type instead. Keep the gate baseline unchanged; parsing and assertions have the same runtime behavior.
Name the JSONL backend and explain that its cache reuses recently decoded logs. This makes the bounded-batch rationale concrete without duplicating private implementation constant names in the operational description.
An unexpected visitor rejection represents a batch-level programming or reporting failure, rather than a normal per-Session refusal. The scheduler already drains its active workers before surfacing it and does not start deferred retries. State that behavior explicitly so callers do not assume the retry pass runs after a fatal batch failure.
The parent-child integration case can succeed without observing a race, while scheduler-only tests do not exercise the actual error classification or CLI output. Inject one genuine source-change error at the persistence open boundary in an isolated child process, then delegate to the real backend. Assert DEFERRED, completion of the other input, one RETRY, preserved source bytes and the resulting parent catalog. This adds no production test hook or timing-based race.
The command promises any positive safe integer, but its additional decimal-spelling regex rejected valid expert overrides such as 1e2 and +1. Let Number parse the supplied value and retain the positive safe-integer check. This preserves the CPU-based default cap while allowing equivalent numeric spellings and larger explicit values.
The source-entry regression creates a Windows junction to the real checkout. Its local unlink normally removes it, but fallback teardown must remain safe if that unlink fails. Use the repository fixture cleanup helper to unlink reparse points before recursive deletion and retain its bounded Windows handle-release retries. This changes only test resource cleanup.
A text error list does not show how failures are distributed across historical source versions or let developers aggregate repeated rejection reasons. Emit and save the same JSON report with per-version outcomes, stable failure groups and per-input diagnostics, plus runtime, checkout and report locations. Keep source versions from physical discovery rather than migrated handle headers.
Classify only known diagnostic fields such as unexpected members, unknown events, child prerequisites and sequence gaps. Preserve original messages and use an explicit fallback for other failures; the report makes no inference about historical producer bugs and cannot influence admission or retry decisions. Regression checks cover printed/file/log equality, private report files, grouped distinct sequence coordinates and source-file preservation.
Sequential corpus migration waits for each Session to finish I/O and publication verification before starting the next. Use a bounded queue with one shared persistence Context, defaulting to available CPU count capped at 16. Expert --jobs overrides accept any positive safe integer. Stable input indices and separate completion counts keep interleaved progress useful for large corpora without opening every log simultaneously.
A concurrently upgraded child can invalidate its parent evidence. Preserve the backend revision checks and defer only the explicit source-change error for one serial retry after initial work drains. Corruption, unsupported formats and writer locks remain failures; historical files and existing V4 data remain unchanged. Deterministic scheduler tests cover 10,000 inputs, bounded overlap and retry ordering, while real raw/zstd corpora verify parent catalogs and serial-equivalent output. Performance timing was stopped at user request because the host has competing workloads.
Give developers one pnpm command to exercise the release migration against their own Session roots. Inventory physical generations so malformed entries remain visible, then reuse JSONL write-open locking, validation, and exclusive successor publication with the full root available for child-catalog evidence. Open existing V4 files read-only and preserve every historical generation.
Report each selected source and outcome, continue after individual failures, repeat all errors at the end, and retain a private temporary report with checkout/runtime details and full causes. A failed run exits nonzero. Explicitly close handles because source-launch await-using lowering inside a loop can retain a previous iteration error. Real-entry regressions cover raw/zstd tool-result conversion, repeated runs, unchanged originals, failure continuation, and unsupported layouts. Document the command in the migration cookbook.
The V4 acknowledgement still said missing child descriptors reject migration. The converter already skips backfill when a child has no single supported own descriptor, while explicit identity, timestamp, and mode conflicts still reject publication. Align both language versions with that behavior so release reviewers and corpus testers do not expect an obsolete refusal. Preserve every machine acknowledgement and schema fingerprint.
The conversation fixture used an undeclared goal source and forced it
through MessageSource with a double assertion. That bypassed the V4
producer-owned source types instead of exercising an input accepted by
createUserMessage.
Use the fixture's existing declared test source for both injected plugin
messages and remove the unused MessageSource import. The resolver excludes
all non-user, non-checkpoint sources, so these cases do not depend on goal
identity or need a goal-package dependency. Message content, event order,
captured sequence numbers, and every projection assertion stay unchanged.
Validation: all 49 session-reference tests pass, the Host TypeScript
aggregate passes, and the diff passes whitespace checks.
The release branch still passed resolutionMode: link from the source CLI
and documented a separate link/runtime selector. The integrated master
has removed that selector and its materializer in favor of one runtime
resolver that also supports source launches. The spread kept compiling
because excess-property checking did not reject it, but the property was
ignored by runProfile and the retained prose contradicted the runtime.
Remove the dead option/comment and its obsolete bilingual paragraph.
The CLI entry and the architecture note now match the integrated master;
keep source-module identity verified through its real source-launch and
tool-execution tests. This retires an old integration workaround, without
changing Session V4 or restoring a second profile resolution backend.
The new master source-tool smoke reads isError and content from a V3
nested tool-result block. After integration with the V4 release, that
block no longer exists: the durable message itself has role tool,
toolCallId, isError and result content. The old assertions fail to
compile and cannot verify the current writer's output.
Read the existing V4 fields while retaining the real source-profile
launch, single Tools instance, call identity, sandbox error and shell
output checks. Do not impose the old single-wrapper cardinality on the
flat content array. This adapts the incoming regression test without
changing the V4 format, migration implementation or stored recordings.
Integrate the master tip observed for the final V4 landing preparation on
top of the already merged migration stack. Preserve the V4 writer, flat
message migration, extension policy, catalog validation and historical
recordings while bringing in upstream profile resolution and desktop/UI
changes.
Resolve four generated translation-pairing records from the combined
English/Chinese text. Keep this master checkpoint separate from any
integration fixes and retain all reviewed parent history. Follow-up
validation covers the affected launch/UI paths and migration replay.
The cookbook previously promoted plugin prefixes and owner separation into a general requirement. That overstates the migration policy: direct source kinds and ordinary metadata need no new namespace solely because they are extensible.
Require each edge to document only the mappings its target needs and why, preserve nested opaque data, and permit alpha refusal of unexpected source fields that would otherwise acquire new target meaning. Exact V3 source and ToolSchema choices remain in the V3-to-V4 README; no runtime, schema, or provider behavior changes.
The convention identifies how an edge preserves the identifiers and extension fields that it explicitly maps; it does not require every extension-owned field in every source slot to be renamed.
Keep the cookbook general enough for preserved stream-start extras while retaining the exact field-owner mappings documented by each migration edge. This changes policy wording only, not the current V4 schema or conversion implementation.
This case calls only liftToolResult; the native validator has separate coverage. Name the behavior actually asserted so the test title does not claim both paths are covered by one assertion. Assertions and production behavior are unchanged.
The flat result conversion prefixes every extension-owned field, not just names currently colliding with core fields. Correct the cookbook wording to match that complete mapping.
Record why retaining opaque message and wrapper owners does not invent a shared owner or a new core metadata field. The deliberate costs are retaining duplicate opaque values and requiring plugin consumers to use V4 names; the first-class tool-message decision and runtime implementation stay unchanged.
The exported converter rejects malformed wrappers with SessionFormatError in addition to refusing unsupported nesting. Document both failure categories so callers can distinguish corruption from a converter support limit.
Remove the obsolete claim that recorded replay compares serialized field order: the native-writer parity assertion compares parsed objects. The emitted fields and their order remain unchanged.