Preserve one durable staged input across terminal close and the worker's early turn flush, without duplicating resumed transcripts or creating a session with a null prompt. Fixes#63766.
Retain a distinct CLI history baseline during the signal window before a turn's normal persistence flush. When CLI history aliases the live agent list, use marker-only persistence so a genuinely unflushed tail is written.
Baseline bars were positioned at their absolute timeline offset, which
included the baseline run's own wait time — making duration comparisons
hard since the bars were visually offset. Now each baseline bar starts
at the same left position as its corresponding current job, so the two
bars directly overlap for at-a-glance duration comparison.
Also removed the now-unused bl_t0 / bl_max / bl_jobs_timed variables
that tracked the baseline timeline position.
Add wait time computation: for each job, wait_s = started_at - max(completed_at
of all jobs that finished before it started). This is a timestamp heuristic
(no workflow YAML dependency parse needed) that's accurate for pipeline-shaped
CI where the critical path is linear at each stage.
Shows up in:
- Job table: new Wait + Δ Wait columns
- Gantt chart: hatched bar segment before the run bar
- Step details: '(wait Xs)' annotation in the summary line
- Markdown summary: Total wait row with delta vs baseline
- Stats: total_wait / bl_total_wait in compute_stats
Also completes the skipped-jobs UI:
- Skipped stat card in stats cards
- Skipped row in markdown summary table
Backward compatible: old cached baselines without wait_s annotate on load.
Skipped jobs (conclusion == 'skipped') have null/zero-duration timestamps
that polluted every downstream computation: they counted as 'unchanged'
(0 vs 0) in faster/slower tallies, showed meaningless '0.0s (0%)' deltas
in the job table, rendered phantom gantt bars, and inflated wall/compute
totals.
Add is_skipped() helper and apply it consistently:
- compute_stats: exclude from wall/compute + faster/slower/unchanged;
add 'skipped' and 'bl_skipped' counts
- _gantt_bars: filter from current bars, baseline bars, and axis calc
- _job_table: show 'skipped' label instead of durations/deltas
- _step_details: skip entirely (no meaningful step data)
- _regressions: exclude from both current and baseline sides
alelpoan's fix (emit hermes:zoom:changed after restore) is correct, but the
bug's root is duplication: setAndPersistZoomLevel and restorePersistedZoomLevel
each independently did setZoomLevel + send, and restore forgot the send.
Collapse both (and the lifecycle re-assert) into a single applyZoomLevel()
helper in zoom.ts that always applies-then-notifies — the regression can't
recur by omitting a send. Replace the source-grep test (which broke on main:
the sibling source-assertion pet test it copied was refactored to a behavioral
one, dropping the fs/path imports it relied on) with behavioral coverage of the
funnel, matching zoom.ts's "unit-testable without booting a BrowserWindow"
convention.
Co-authored-by: alelpoan <alelpoan@proton.me>
classifyNativeBinary only checked big-endian Mach-O/Fat magic bytes
(feedfacf, feedface, cafebabe). Real Darwin .node files from node-pty
prebuilds are stored little-endian on disk (cffaedfe = MH_CIGAM_64),
so every Darwin prebuild classified as null, and validateStagedBinaries
threw a platform mismatch on macOS — breaking npm run check for every
macOS contributor. CI didn't catch it because runners are Linux (ELF
path was correct) and tests only planted big-endian fake headers.
Add recognition for all six Mach-O/Fat byte orderings:
- MH_CIGAM (cefaedfe) — LE 32-bit
- MH_CIGAM_64 (cffaedfe) — LE 64-bit [the one real prebuilds use]
- FAT_CIGAM (bebafeca) — LE universal
Update makeFakeNode to write LE CIGAM_64 bytes for the darwin fixture
(matching real on-disk format) and add regression tests for all new
magic forms.
The set-matrix step wrote the npm workspace query result directly to
$GITHUB_OUTPUT. If discovery ever produced [], the matrix would expand
to zero check jobs, leaving the reusable workflow green without running
any JS/TS checks.
Now the step validates the result is a non-empty array before emitting
it, and exits 1 with a GitHub annotation if it's empty or jq failed.
stageNodePty received electron-builder's { platform, arch } but unconditionally
copied host build/Release, staging a host binary (e.g. macOS Mach-O) for a
foreign target (e.g. linux-arm64). The fallback rebuild also didn't pass the
target arch to electron-rebuild.
Now:
- build/Release is only staged when target platform+arch match the host
- cross-platform targets with no matching prebuild fail closed
- same-platform different-arch rebuild passes --arch to electron-rebuild
- post-staging validation reads .node magic bytes (ELF/Mach-O/PE) and rejects
any binary whose platform doesn't match the target
Adds stageNodePtyInto() (testable core) and classifyNativeBinary() (pure),
plus 11 regression tests covering the cross-target scenarios.
Move setGatewayState + rerender calls inside act() blocks and make the
synchronous soft-switch test async so all state updates are wrapped.
Eliminates the last 4 act() warnings (44 → 0).
Extracted zoomWiringForWindowKind() + ZOOM_WINDOW_CONFIG into zoom.ts so
the pet-overlay-
opts-out / chat-windows-keep-zoom contract is tested via the pure
config,
not by reading source. Callers in main.ts now use
zoomWiringForWindowKind()
instead of inline { zoom: false } / default { zoom: true }.
When neither a prebuild nor a compiled build/Release/*.node is found for
the target platform-arch, stage-native-deps.mjs now runs
electron-rebuild -f -w node-pty to compile one from source before
re-copying build/Release into the staged dist.
This makes the staging script self-sufficient — it always produces a
working native binary dir regardless of whether npm ci --ignore-scripts
skipped postinstall or whether node-pty publishes prebuilds for the
target (e.g. linux-x64 has no prebuild).
Add vitest.setup.ts with IS_REACT_ACT_ENVIRONMENT=true + auto-cleanup,
and wrap render()/fireEvent() calls in act() across 8 test files:
- provider-config-panel.test.tsx: wrap renderPanel + fireEvent in act
- providers-settings.test.tsx: wrap renderProvidersSettings + fireEvent
in act
- use-prompt-actions/index.test.tsx: add actRender helper, wrap all 38
render
calls, wrap Harness handle methods (submitText/cancelRun/steerPrompt/
restoreToMessage) in act at the onReady callback level
- attachments.test.tsx: make renderWithI18n async + wrap in act
- skills/index.test.tsx: make renderSkills async + wrap fireEvent in act
- messaging/index.test.tsx: make renderMessaging async + wrap fireEvent
in act
- gateway-connecting-overlay.test.tsx: wrap all render/rerender in act
- preview-pane.test.tsx: wrap render calls in act, make tests async
Reduces act warnings from 44 to 4 (remaining are fake-timer + pre-render
store mutation edge cases). All 146 test files / 1180 tests still pass.