Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7caa96f
docs: define winTerm workspace architecture
HelloThisWorld Jul 15, 2026
ec7d525
feat: add workspace model and persistence
HelloThisWorld Jul 15, 2026
358b12a
feat: add workspace capture and restore services
HelloThisWorld Jul 15, 2026
4f9a595
test: add workspace restore and recovery coverage
HelloThisWorld Jul 15, 2026
25cf6b4
chore: prepare winTerm 0.4 workspace release
HelloThisWorld Jul 15, 2026
8988f4a
ci: satisfy workspace spelling checks
HelloThisWorld Jul 15, 2026
a152bd2
ci: allow WPF dependency restore on Linux
HelloThisWorld Jul 15, 2026
73d5eba
ci: run Windows build on hosted runners
HelloThisWorld Jul 15, 2026
bc411dd
ci: gate pull requests on full Windows build
HelloThisWorld Jul 15, 2026
768b0c0
ci: accept JSON integer types in asset validation
HelloThisWorld Jul 15, 2026
91ed0b0
ci: preserve workspace timestamps as JSON strings
HelloThisWorld Jul 15, 2026
8bc1ace
ci: pin Windows build to the upstream SDK image
HelloThisWorld Jul 15, 2026
cc90676
build: restore slnx dependencies with supported tools
HelloThisWorld Jul 15, 2026
1f9602e
fix: resolve workspace build errors
HelloThisWorld Jul 15, 2026
9ad9688
ci: build focused Windows targets
HelloThisWorld Jul 15, 2026
8adedde
ci: disable XAML fingerprinting on clean builds
HelloThisWorld Jul 15, 2026
f5882bd
ci: capture XAML compiler diagnostics
HelloThisWorld Jul 15, 2026
650e0e6
ci: summarize XAML task diagnostics
HelloThisWorld Jul 15, 2026
d53bf82
ci: normalize XAML include paths
HelloThisWorld Jul 15, 2026
11b4d86
fix: decouple theme serialization from WinRT internals
HelloThisWorld Jul 15, 2026
c1b6aa5
fix: keep theme origin updates inside settings model
HelloThisWorld Jul 15, 2026
8e1a908
fix: serialize imported themes through public ABI
HelloThisWorld Jul 15, 2026
5666bb4
fix: keep branding directives outside macro calls
HelloThisWorld Jul 15, 2026
c2fdb03
fix: link the shell shim as a console app
HelloThisWorld Jul 15, 2026
2bb2c86
fix: package the shell shim from its build output
HelloThisWorld Jul 15, 2026
25d42b4
ci: supply SolutionDir when building test projects directly
HelloThisWorld Jul 16, 2026
4e45e33
fix: accept #-prefixed shorthand theme colors
HelloThisWorld Jul 16, 2026
a43f483
fix: count default-profile substitution as a pane restore failure
HelloThisWorld Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/actions/spelling/expect/winterm-workspaces.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
autorun
environmentvariables
executablepath
externalfile
privatekeypath
shellarguments
sshprivatekey
startupcommand
13 changes: 11 additions & 2 deletions .github/workflows/winterm-full-build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: winTerm full Windows build

on:
pull_request:
workflow_dispatch:

permissions:
Expand All @@ -9,7 +10,7 @@ permissions:
jobs:
build-and-test:
name: x64 ${{ matrix.configuration }} build and relevant tests
runs-on: [self-hosted, windows, x64, winterm-build]
runs-on: windows-2022
timeout-minutes: 180
strategy:
fail-fast: false
Expand All @@ -36,6 +37,10 @@ jobs:
shell: pwsh
run: .\scripts\winterm\test-shell-integration.ps1 -Shell All

- name: Validate workspace fixtures and security boundaries
shell: pwsh
run: .\scripts\winterm\validate-workspaces.ps1 -BundledFixtures

- name: Build x64
shell: pwsh
run: .\scripts\winterm\build.ps1 -Configuration ${{ matrix.configuration }} -Platform x64 -IncludeTests
Expand All @@ -44,10 +49,14 @@ jobs:
shell: pwsh
run: .\scripts\winterm\test.ps1 -Suite Relevant -Configuration ${{ matrix.configuration }} -Platform x64

- name: Run workspace benchmark
shell: pwsh
run: .\scripts\winterm\benchmark-workspace.ps1

package:
name: x64 Release MSIX
needs: build-and-test
runs-on: [self-hosted, windows, x64, winterm-build]
runs-on: windows-2022
timeout-minutes: 180

steps:
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/winterm-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,11 @@ jobs:
- name: Test PowerShell and Command Prompt integration
shell: powershell
run: .\scripts\winterm\test-shell-integration.ps1 -Shell All

- name: Validate workspace fixtures, restore, recovery, and import security
shell: powershell
run: |
.\scripts\winterm\test-workspace-model.ps1
.\scripts\winterm\test-workspace-restore.ps1
.\scripts\winterm\test-workspace-recovery.ps1
.\scripts\winterm\test-workspace-import.ps1
2 changes: 1 addition & 1 deletion docs/branding.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
| Execution alias | `winterm.exe` |
| Forbidden alias | `wt.exe` |
| Development publisher | `CN=winTerm Development` |
| Package version | `0.3.0.0` |
| Package version | `0.4.0.0` |
| Release status | `0.2.0-dev` until v0.2 acceptance is complete |
| Package description | `Independent open-source terminal based on Microsoft Windows Terminal` |

Expand Down
11 changes: 11 additions & 0 deletions docs/crash-recovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Crash recovery

winTerm workspace recovery uses a running marker, a clean-shutdown marker, the last valid session file and backup, and retained snapshots. Startup must inspect the previous markers before writing the new running marker.

A normal shutdown stops accepting workspace mutations, captures the final generation, atomically saves Last Session, flushes it, writes the clean marker, removes the running marker, and only then completes window shutdown. A crash leaves the running marker and therefore cannot be described as a normal exit.

Snapshots are written on the periodic policy and before destructive or migration operations. The default retention is the newest three valid files. Snapshot names contain a fixed-width generation so selection is deterministic. If the newest snapshot is corrupted, recovery examines older snapshots. Loading a damaged Last Session tries the last validated backup; damaged managed files can be moved to quarantine with sanitized reason metadata.

Recovery inspection recommends the latest valid snapshot after an abnormal shutdown. Following a clean shutdown, it recommends Last Session; when neither is available, it recommends a new session. It never overwrites normal state without a later explicit recovery choice. A recovered pane can report that a command was running, but no command is stored or replayed and no process is reattached.

Marker, backup, retention, latest-valid selection, and recommendation logic are implemented and source-tested. The recovery prompt and forced-termination manual scenario require a built application.
11 changes: 11 additions & 0 deletions docs/multi-monitor-restoration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Multi-monitor restoration

Every window stores absolute bounds, normalized monitor-relative bounds, monitor device and stable identifiers, friendly name, work area, and horizontal and vertical DPI. Display ordinal names alone are not treated as stable identity.

Resolution attempts exact device ID, stable monitor ID, friendly name plus work-area resolution, physical-position similarity, and a safe available monitor. When the original display cannot be used, normalized bounds are applied to a safe monitor. Same-monitor bounds with materially different DPI are scaled from the saved work area and DPI.

The safety pass rejects non-finite or non-positive geometry, constrains size to the current work area, applies minimum logical dimensions of 480 by 320 where the work area permits, and keeps at least 64 logical pixels of the title area reachable. Negative virtual-desktop coordinates remain valid. An invalid rectangle becomes a centered safe default.

Minimized windows restore as normal unless explicitly enabled. Fullscreen restore is also opt-in; otherwise it restores as normal before any later user action. Monitor and DPI adjustments are recorded in the restore report.

The pure resolver has automated source fixtures for missing-monitor, DPI, work-area, and off-screen behavior. Mixed-DPI physical displays still require manual testing on a built package.
11 changes: 11 additions & 0 deletions docs/named-workspaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Named workspaces

Named workspaces are durable templates stored under `workspaces/named`. Their generated ID, not their display name, forms the local file name. Names are bounded data and cannot affect paths.

The manager supports explicit Save As, Rename, Duplicate, Delete, and Set Default operations. Save As removes recovery state and creates user-owned metadata. Duplicate requires a new ID and timestamps and never inherits default status. Rename changes only display metadata. Setting a default clears the flag on other named workspaces.

Delete first creates a recovery snapshot of the current runtime state. It deletes only the named definition and does not close or delete running shell sessions. Named files are never updated by autosave; Last Session is the autosave target.

`index.json` contains ID, name, relative file name, update time, default state, and health. It is rebuilt from valid named files and is not authoritative. A damaged index therefore cannot permanently hide the workspace definitions.

The backend operations are implemented. Gallery cards, confirmation dialogs, and user interaction remain runtime UI work requiring a built application.
2 changes: 1 addition & 1 deletion docs/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Run source validation first:

## 2. Set release identity

For a local development package, the v0.3 manifest uses version `0.3.0.0` and publisher `CN=winTerm Development`.
For a local development package, the v0.4 manifest uses version `0.4.0.0` and publisher `CN=winTerm Development`.

For a public release:

Expand Down
98 changes: 98 additions & 0 deletions docs/v0.4-acceptance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# winTerm v0.4 acceptance

## Verdict

Last updated: 2026-07-15.

**Not accepted.** The repository now contains a substantial Workspace Restore schema and backend foundation, strict storage and import boundaries, resolvers, source fixtures, scripts, CI wiring, and documentation. This machine has no PowerShell 7, Visual Studio, MSBuild, `cl.exe`, built winTerm executable, or package, so compiled tests, Debug/Release builds, launch, Settings UI, real multi-window restore, shell launch, mixed-DPI hardware, and crash-dialog scenarios cannot be claimed as passed.

`[x]` means the exact source or script check was run in this checkout. `[ ]` means unavailable, not integrated end to end, or not manually tested.

## Baseline

| Item | Recorded value |
| --- | --- |
| Branch before v0.4 work | `main` |
| Commit before v0.4 work / v0.3 baseline | `183414406621cbe83420014012c880caf3248509` |
| Microsoft Terminal source baseline | `release-1.25@1cea42d433253d95c4487a3037db48197b5e72f4` |
| Git remotes | `origin` exists; `upstream` is absent |
| Uncommitted files before v0.4 work | None observed |
| Existing persisted layout | Upstream `ApplicationState`, `WindowLayout`, `TerminalPage::PersistState`, `Tab::BuildStartupActions`, and `Pane::BuildStartupActions` |
| Existing state location | Branding-specific Settings Model application state; v0.4 store adds its own `workspaces` child |
| Existing shell CWD metadata | `TermControl::WorkingDirectory`, OSC 9;9, v0.3 Shell Integration metadata |
| Workspace schema | `1` |
| Application/package version | `0.4.0-dev` / `0.4.0.0` |

- [x] v0.1, v0.2, and v0.3 acceptance documents exist.
- [x] Repository state and upstream persisted-layout implementation were inspected.
- [x] Package identity remains `Kaname.winTerm` and the alias remains `winterm.exe`.
- [ ] The `upstream` remote exists.
- [ ] v0.1, v0.2, or v0.3 runtime acceptance is complete. Their unchecked items remain unchecked.

## Implemented and source-checked

- [x] Schema version 1 models windows, tabs, pane/split layouts, profiles, CWD namespaces, appearance, focus, monitor/DPI data, and non-process session flags.
- [x] Validator covers global ID uniqueness, active references, cycles, missing/duplicate pane references, count/depth limits, finite ratios and geometry, DPI, enums, and WSL path namespace.
- [x] Deterministic UTF-8 serializer uses strict JSON options and rejects special floats, duplicate keys, trailing input, oversize files, and excessive nesting.
- [x] Atomic writer, validated backup, last-session store, fixed-width snapshots, retention, rebuildable named index, markers, quarantine metadata, and stale-generation rejection are implemented.
- [x] Capture privacy and CWD-priority policy are implemented over a value snapshot.
- [x] Profile, directory, theme, font, monitor, DPI, safe-bounds, and progressive restore-plan resolvers are implemented.
- [x] Named Save As, Rename, Duplicate, Delete-with-snapshot, and Set Default backend operations are implemented.
- [x] Import rejects command, executable, script, environment, registry, URL, include, plugin, credential, and private-key fields. Newer schema is not loaded.
- [x] Export supports working-directory omission, known home-prefix redaction, monitor metadata omission, title omission, and atomic output.
- [x] Diagnostics provide health/count/generation data and redact the user component of local paths.
- [x] Startup settings and fallback resolution for new session, Last Session, default workspace, ask, and recovery choice are implemented as backend models.
- [x] The winTerm-branded Settings Model defaults the inherited safe startup path to persisted layout; other upstream branding retains its original default-profile behavior.

## Executed checks

| Check | Result |
| --- | --- |
| `validate-workspaces.ps1 -BundledFixtures` | Passed: 2 valid, 1 legacy migration input, 1 invalid, 1 corrupted, and 3 security fixtures classified as expected |
| `test-workspace-model.ps1` | Passed |
| `test-workspace-restore.ps1` | Passed |
| `test-workspace-recovery.ps1` | Passed |
| `test-workspace-import.ps1` | Passed, including a generated file larger than 5 MB |
| `benchmark-workspace.ps1` | Passed with locally measured PowerShell JSON timings; this is not application startup performance |
| `test.ps1 -Suite Smoke` with process-scoped execution-policy override | Passed |
| `git diff --check` | Passed at the recorded test point |
| Compiled `WinTermWorkspaceTests` | Added, not run: compiler and test host unavailable |
| `build.ps1 -Configuration Debug -Platform x64 -IncludeTests` | Failed before build: PowerShell 7 is unavailable |
| `build.ps1 -Configuration Release -Platform x64 -IncludeTests` | Failed before build: PowerShell 7 is unavailable |
| `package.ps1 -Platform x64 -SkipBuild` | Failed as expected: no package output exists |
| MSIX launch | Unavailable |

### PowerShell harness measurements

These measurements exercise fixture construction plus PowerShell JSON conversion, not the compiled application, UI thread, shell launch, time to first window, time to active pane, or peak memory.

| Scenario | Capture ms | Serialization ms | Restore parse/count ms | Result |
| --- | ---: | ---: | ---: | --- |
| 1 window / 1 pane | 19.853 | 219.526 | 23.909 | Passed |
| 1 window / 20 tabs | 1.329 | 0.324 | 0.913 | Passed |
| 4 windows / 40 panes | 0.570 | 0.384 | 3.030 | Passed |
| 8 windows / 100 panes | 3.562 | 1.030 | 5.856 | Passed |

## Open acceptance gates

- [ ] Workspace backend compiles in Debug and Release x64 and all compiled unit tests pass.
- [ ] Capture is wired to actual multi-window UI mutation events without typing, drag, or resize latency.
- [ ] Last Session and periodic snapshots are written and loaded by the running application.
- [ ] Startup and exit settings, workspace gallery/editor, import summary/confirmation, recovery prompt, restore report, and diagnostics views are present and manually exercised.
- [ ] Actual windows, positions, sizes, maximized/fullscreen policy, active window, and mixed-DPI monitor changes restore.
- [ ] Actual tab order, pane layout/ratio, focused and zoomed panes, profiles, working directories, themes, and fonts restore for PowerShell, CMD, and available WSL profiles.
- [ ] A failed pane is isolated in the running application.
- [ ] Forced termination offers recovery and does not overwrite normal state without consent.
- [ ] Packaging, installation, signing status, coexistence, and Windows Terminal isolation are verified.

## Known limitations

- Running processes, SSH connections, Vim/REPL state, handles, sockets, command history, terminal output, clipboard data, and full scrollback are not restored.
- The current source establishes the backend and inherited layout bridge boundary; end-to-end runtime wiring and Settings UI remain open.
- UNC validation requires a background caller. Remote CWD is metadata only and never reconnects SSH.
- WSL matching depends on existing discovered distributions and has not been tested on this machine.
- Monitor identity matching remains limited by identifiers exposed by the eventual host adapter.
- Fullscreen restore is opt-in and has not been manually tested.
- Large-workspace PowerShell JSON measurements are not a substitute for built application startup, active-pane, memory, or UI-latency measurements.

Do not change this verdict until the open gates have actual build, package, launch, runtime, hardware, and manual evidence.
21 changes: 21 additions & 0 deletions docs/workspace-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Workspace architecture

winTerm v0.4 treats a workspace as a restore description, not a suspended terminal process. The description contains windows, tab order, the existing pane split tree, profile references, working-directory metadata, appearance references, focus, and recovery metadata. It never represents process memory, handles, sockets, terminal output, command history, clipboard data, credentials, or environment snapshots.

Three states are deliberately separate:

- A named workspace is a user-managed definition and changes only through an explicit save or update operation.
- Runtime state is the currently open set of windows, tabs, and panes. Opening a named workspace does not make later runtime changes overwrite that definition.
- Last Session is the autosave target for the latest runtime state and may include changes made after opening a named workspace.

The source boundary is under `src/winterm/Workspaces`. `Model` defines immutable snapshot data. `Capture` applies privacy policy to a UI-thread snapshot. `Persistence` performs strict JSON parsing, validation, migration, atomic replacement, backup, snapshots, indexing, and quarantine. `Restore` resolves monitors, profiles, directories, themes, and fonts into a progressive plan. `Runtime` owns dirty generations, debounce decisions, recovery inspection, and explicit named-workspace operations. `ImportExport` is the untrusted-file boundary. `Diagnostics` emits redacted health data.

The Microsoft Terminal `ApplicationState`, `WindowLayout`, and pane startup-action implementation remain the inherited runtime bridge. v0.4 does not change ConPTY, the VT parser, text buffer, renderer core, Unicode width engine, input protocol parser, or OpenConsole internals. The backend is included in the Settings Model library project so it can be shared by startup, settings, and tests without serializing live XAML or Pane objects; compilation is still an open acceptance gate on this machine.

For winTerm branding only, the inherited first-window preference defaults to persisted layout. Other upstream branding retains the original default-profile setting. On a first launch with no persisted layout this still opens a new default-profile session.

Capture must read the minimum UI state on the UI thread, produce a value snapshot, and perform validation, JSON serialization, and disk writes away from UI objects. Every save has a monotonically increasing generation; an older generation cannot replace a newer one.

The storage layout is a winTerm-only `workspaces` directory under the application state root. It contains `last-session.json`, its backup, `snapshots`, `named`, `imported`, `quarantine`, and a rebuildable `index.json`. This storage never uses Microsoft Terminal package identity or LocalState.

Visual docking, corner docking, `GridNode`, `EmptySlotNode`, cross-window live pane transfer, terminal broker support, ConPTY reattachment, command replay, and SSH reconnection remain outside this architecture.
13 changes: 13 additions & 0 deletions docs/workspace-capture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Workspace capture

Workspace capture begins with a value-only snapshot assembled from window, tab, and pane state. Live XAML, Pane, TermControl, connection, and process objects are never serialized. The capture service applies privacy settings before a snapshot reaches persistence.

For each pane, the working-directory selection order is fresh Shell Integration metadata, last known Shell Integration metadata, profile starting directory, process launch directory, and user home. The descriptor records both the namespace and source. Remote metadata is omitted by default. If working-directory saving is disabled, both the current directory and starting-directory fallback are removed.

Capture includes stable window, tab, and pane IDs, active references, the pane/split layout, profile references, theme and font overrides, and only `lastExitCode`, `wasCommandRunning`, and `endedUnexpectedly` session flags. It never reads or writes command text, command history, terminal output, clipboard content, environment variables, credentials, SSH state, or process memory.

Structural changes use a 750 ms debounce. Window move/resize and CWD changes use 1000 ms. Final exit capture is immediate. Periodic recovery snapshot policy defaults to 30 seconds. Mouse-move and resize events only mark state dirty; they do not write each event.

The dirty tracker assigns a generation to every mutation. Serialization and disk I/O may finish out of order, but completion is accepted only when it matches the newest generation. This prevents generation 42 from overwriting generation 43.

The current source contains the capture/privacy service and scheduler policy. Actual UI event wiring and performance validation require a built winTerm package and remain open in `v0.4-acceptance.md`.
Loading
Loading