# v0.4.0 Automation execution plan

> Status: authoritative and resumable.
>
> Integration branch: `agent/v0.4-automation-integration`
>
> Frozen Phase 1 baseline: PR #70 head
> `b4308ee73dfb6206a61fb71e6f70b4447656af05`

This file is the source of truth for the v0.4.0 engineering work. After a
context reset, read this file, the final integration PR description, and the
current Git status before continuing from the first incomplete gate.

## 1. Live baseline

Recorded on 2026-08-03 (Asia/Hong_Kong):

- `origin/main`: `ca347b915026ac44b5f9e63a3224bc4eabe53e6f`
- PR #70: open Draft, base `main`, head `b4308ee73dfb6206a61fb71e6f70b4447656af05`
- PR #70 CI: run `30808409605`, conclusion `success`
- PR #70 review: Codex found no major issue at `b4308ee`; no unresolved
  review threads
- PR #70 manual smoke checkbox is still unchecked, so no historical GUI smoke
  result is accepted as final evidence
- Issue #69: open; partial rollout and global-state writes are not reliably
  visible to the outer rollback path
- PR #62: closed, unmerged, superseded by #70; design reference only
- final integration branch did not exist remotely before this worktree was
  created

Local isolated baseline at `b4308ee`:

- Node: 110 passed
- Core: 91 passed, 1 conditional WSL-only test skipped
- Application: 9 passed
- WinForms: 29 passed
- release version verification for v0.3.2: passed
- `git diff --check`: passed

Evidence is under the ignored `artifacts/evidence/baseline-b4308ee-*` directory.

Current integration evidence recorded on 2026-08-04:

- implementation commit `28c4dd4` passed the visible Windows Release Headful
  gate on the interactive input desktop
- manifest coverage: 40/40; required Headful scenarios: 53/53; errors/blockers:
  0/0; 17 real dialogs were handled; published EXE SHA-256 independently
  matched
- the evidence contains real native dialogs, sync/switch/restore/prune file and
  SQLite effects, prune physical-deletion/sentinel checks, restart persistence,
  and GUI-to-Application operation/lifecycle traces
- Node passed 183/183; Core passed 188 with one expected WSL environment gate;
  Application passed 49/49, Automation 27/27, WinForms 66/66, and GUI contract
  tests 36/36; the Release solution built with zero warnings and errors
- the post-review hardening requires explicit relocation targets, publishes and
  revalidates automatic-prune deletion targets, and reserves exit code 5 for
  failures with complete-rollback evidence; independent stable-diff review found
  no P0/P1/P2

## 2. Architecture and ownership

The required dependency direction is:

```text
Core storage and domain operations
             ^
Application use cases and immutable operation state
             ^                         ^
Business Automation host          WinForms event adapter
                                         ^
                                  real WinForms controls
                                         ^
                                  GUI Automation bridge
```

Responsibilities:

- Core owns config, rollout, SQLite, global state, backups, transaction
  primitives, restore, storage resolution, locking, and WSL safety.
- Application owns status, diagnostics, plan, sync, switch, restore, prune,
  settings/provider changes, operation serialization, cancellation, immutable
  inputs, plan freshness, and structured outcomes.
- WinForms owns rendering, native/platform dialogs, focus, external folder
  launching, and conversion of real control events to Application calls.
- Business Automation calls Application use cases directly and never copies
  Core workflows.
- GUI Automation drives actual control instances on the UI thread and records
  the control-event-to-Application causal chain. It may not call Application
  directly while claiming a control was exercised.

## 3. Transaction and recovery model (Issue #69)

Both Node and .NET implementations use equivalent semantics:

1. Acquire the existing per-Codex-Home operation lock.
2. Refuse a new write if an unresolved transaction journal exists.
3. Scan and fingerprint targets, validate SQLite, and create the managed
   backup before any target mutation.
4. Create a durable transaction journal under
   the operation's managed backup as `transaction-journal.jsonl` before the
   first target mutation.
5. Stage replacement content beside each target, flush it, and use a
   same-directory atomic replacement. Each append-only journal record is
   flushed before the associated target transition.
6. Before replacing each target, record `applying`; after replacement and
   metadata restoration, record `applied`. The outer coordinator receives the
   durable applied list even when the batch throws.
7. SQLite remains protected by its own transaction. Rollout/global-state file
   mutations are compensated from the managed backup if any normal failure or
   cancellation occurs.
8. Rollback records each target independently. If rollback itself fails, keep
   the journal and return the original error, rollback errors, backup path,
   incomplete targets, and safe recovery instructions.
9. A completed operation atomically marks the journal `committed`; a completed
   rollback marks it `rolledBack`. Terminal journals may be retained as audit
   evidence and pruned with their managed backup.
10. Startup/status checks detect non-terminal or truncated journals. Read-only
    diagnostics remain available and show the bound backup; mutations are
    blocked until an explicit restore marks that journal rolled back. No
    silent half-complete state is accepted, and pruning protects every backup
    referenced by a non-terminal journal.

The fault-injection matrix covers backup failure, staging failure, first and
Nth target failure, atomic replacement failure, global-state primary/backup
partial failure, rollback failure, cancellation, crash/restart recovery,
concurrency, duplicate execution, idempotent success, and the ordinary success
path. Tests inspect disk state independently of returned objects.

`--apply` remains unavailable until this matrix passes in both implementations.

M1 evidence at the first implementation checkpoint: Node `120/120`; Core
`101 passed / 1 pre-existing WSL-only conditional skip`. Both ran with fresh
temporary HOME/UserProfile/Codex/SQLite/AppData/Temp/cache roots and inherited
credential-like environment variables removed.

## 4. Application use cases

Application exposes one `IApplicationService` boundary with immutable request
and result records for:

- `Describe`
- `GetStatus` / diagnostics
- `CreatePlan`
- `Sync`
- `Switch`
- `Restore`
- `Prune`

Every operation has an `operationId`, lifecycle state, structured warnings and
errors, cancellation, and one-at-a-time concurrency protection. WinForms and
Business Automation use the same implementation. Existing #70 behavior is a
regression gate: provider fallback, three model modes, busy locking, immutable
snapshots, no await-time parameter drift, refresh serialization, and hard
blocking after a failed storage refresh.

## 5. Business Automation protocol

The Windows Release build contains `CodexProviderSync.Automation.exe` and
`automation-protocol-v0.4.schema.json`.

Protocol family: experimental `0.4`; one process invocation emits exactly one
JSON result on stdout. Diagnostics go to stderr. Commands:

- `describe`
- `status`
- `plan`
- `sync`
- `switch`
- `restore`
- `prune`

Write commands default to plan-only. Mutation requires all of:

- explicit `--apply`
- a plan document produced by the same protocol version
- the exact SHA-256 plan digest
- an unexpired plan
- matching normalized inputs, target paths, target fingerprint, and isolated
  root policy

Restore relocation additionally requires the explicit trio `--sqlite-home`,
`--allow-sqlite-home-relocation`, and `--no-config`. Sync and switch plans list
each concrete automatic-prune deletion target; checked cleanup must match that
exact set and its recursive fingerprints before any planned backup is deleted.

Exit codes are stable within protocol 0.4: `0` success, `2` validation or
usage, `3` stale/invalid plan, `4` target busy/concurrency, `5` operation
failed but rollback completed, `6` recovery required/rollback incomplete,
`7` cancellation/timeout, and `10` internal protocol failure.
Code `5` requires explicit complete-rollback evidence. A failure before the
Application reaches `Applying` is reported as `2`; an applying failure without
rollback or recovery evidence fails closed as `10`.

Machine-readable results distinguish `success`, `warning`, `failure`,
`rollback`, and `recovery`. Unknown commands, malformed input, unsupported
capabilities, path escape, timeout, cancellation, stale plans, and duplicate
execution are schema-tested. No command reads or modifies `auth.json`.

## 6. GUI interaction inventory

Static MainForm entries currently discovered:

| Region | Stable ID | Type | Actions | Capability |
| --- | --- | --- | --- | --- |
| storage | `storage.codexHome` | ComboBox | get/set/select | settings + refresh input |
| storage | `storage.codexHome.browse` | Button | invoke | dialog service |
| storage | `storage.sqliteHome` | TextBox | get/set | settings + refresh input |
| storage | `storage.sqliteHome.browse` | Button | invoke | dialog service |
| storage | `status.refresh` | Button | invoke | refresh |
| status | `status.output` | RichTextBox | get | ui-only result rendering |
| provider | `provider.list` | ListView | get/select | provider selection |
| provider | `provider.manualId` | TextBox | get/set | manual provider input |
| provider | `provider.addManual` | Button | invoke | add provider |
| provider | `provider.removeManual` | Button | invoke | remove provider |
| execution | `execution.updateConfig` | CheckBox | get/set/toggle | sync/switch mode |
| execution | `execution.model.followProvider` | RadioButton | get/set | model mode |
| execution | `execution.model.keepCurrent` | RadioButton | get/set | model mode |
| execution | `execution.model.custom` | RadioButton | get/set | model mode |
| execution | `execution.customModel` | TextBox | get/set | model input |
| restore | `restore.includeConfig` | CheckBox | get/set | restore options |
| restore | `restore.includeDatabase` | CheckBox | get/set | restore options |
| restore | `restore.includeSessions` | CheckBox | get/set | restore options |
| backup | `backup.retentionCount` | NumericUpDown | get/set | retention |
| operation | `operation.execute` | Button | invoke | sync/switch |
| restore | `restore.execute` | Button | invoke | restore |
| backup | `backups.openDirectory` | Button | invoke | ui-only shell boundary |
| backup | `backups.prune` | Button | invoke | prune |
| update | `updates.check` | Button | invoke | update check |
| logs | `logs.openDirectory` | Button | invoke | ui-only shell boundary |

Runtime enumeration is authoritative and may add stable IDs for containers,
focusable status elements, application-owned dialogs, and keyboard commands.
Provider rows use template `provider.row` with a normalized provider ID hash as
`instanceKey`; visible text and list order never participate in identity.

Native folder-picker internals are exempt because Windows owns them. The real
browse button event and real `FolderBrowserDialog` still run; the external
Headful driver selects a path inside the isolated root. Shell-open actions use
an isolated launcher in Automation mode and verify the requested isolated
path.

## 7. GUI bridge and safety

The bridge is disabled in normal mode. Automation launch requires an isolated
root containing the sentinel `.codex-provider-sync-test-root` and a
bootstrap descriptor created with user-only access. The descriptor contains a
random pipe name and one-time token; the token is never placed on the command
line or written to ordinary logs.

Transport is a Windows named pipe with `PipeOptions.CurrentUserOnly`, one
authenticated client, request allowlists, bounded messages, timeouts, and
replay rejection. Paths must remain under the canonical isolated root. Normal
mode, missing/wrong/replayed credentials, a second client, path traversal, and
non-isolated dialog/capture paths are rejected.

Capabilities:

- `ui.launch` (harness/bootstrap)
- `ui.describe`
- `ui.snapshot`
- `ui.get`
- `ui.set`
- `ui.invoke`
- `ui.wait`
- `ui.shutdown`

Commands marshal to the actual WinForms UI thread. `ui.invoke` uses real
control APIs (`PerformClick`, selection/text/check changes, and real window
messages for keyboard paths). The external Headful driver operates real native
and application-owned dialogs while the pending `ui.invoke` observes the real
event and Application completion path. A deterministic operation gate can hold
a real use case to test busy, disabled, cancel, and timeout states.

Each business action emits:

```text
automationId -> GUI event -> operationId -> Application capability -> result
```

with UI-thread identity, event name, timestamps, structured outcome, redacted
values, and schema-2 Application operation ID/kind/lifecycle records.

## 8. Manifest and coverage gate

The static manifest records window/region, control type, actions,
visible/enabled conditions, Application capability or `ui-only`, risk,
scenario IDs, and narrow exemptions. Runtime snapshots record created
instances, visible/enabled/busy state, safe values/options, dialogs, and active
operations.

The gate fails for an unregistered interactive control, duplicate ID, missing
template, unused manifest entry, unexecuted declared action, missing E2E
mapping, missing business trace, or unjustified exemption. Required coverage is
100% for declarations, runtime instances, and declared entry actions.

Generated matrix:

```text
window -> automationId/templateId -> control -> action -> API
       -> Application capability/ui-only -> test -> trace -> result
```

## 9. Isolated verification and one-command regression

Every test process receives an explicit allowlisted environment with temporary
`HOME`, `USERPROFILE`, `CODEX_HOME`, SQLite Home, AppData, LocalAppData, Temp,
NuGet cache, npm cache, settings, backups, logs, captures, and fixture provider
URLs using `example.invalid`. Credential-like inherited variables are removed.
Every root contains the sentinel and every candidate read/write path is checked
before launch. Tests refuse to start if isolation cannot be proven.

The one-command Windows gate is invoked with:

```powershell
pwsh ./scripts/run-windows-gui-e2e.ps1
```

It performs Release publish, isolated fixture creation, real visible GUI
launch, bridge authentication, manifest traversal, all Business and GUI
scenarios, filesystem/result equivalence, trace/evidence/coverage generation,
restart verification, and safe shutdown. A hidden or skipped run is not a
Headful pass. Implementation commit `28c4dd4` passed the full-entry gate with
40/40 manifest entries and 53/53 required scenarios; relevant implementation
changes require a new run.

Formal matrix:

- all existing Node/.NET tests
- transaction fault injection and crash recovery
- Application unit and concurrency/cancellation tests
- Business host process/schema/exit-code tests
- bridge protocol and security tests
- manifest/runtime/action/trace gate
- sync/switch/restore/prune Business-vs-GUI filesystem equivalence
- Windows Release publish and published-host invocation
- real visible Windows Release GUI full-entry E2E and restart
- macOS Core/Application build compatibility
- release version and package-content verification
- GitHub CI at the exact final head

## 10. Version, packaging, and compatibility

All shipped npm/.NET projects move together to `0.4.0`. The protocol remains
experimental `0.4`; manifest and protocol versions identify incompatible
changes. The Windows ZIP includes the GUI, Automation host, schema, and static
manifest. No formal tag or Release is created in this work.

## 11. PR #62 disposition

Retained: layering, shared application behavior, structured machine protocol,
plan/apply, explicit write opt-in, temporary fixtures, stable identifiers,
schema validation, and package verification.

Changed: no stable JSONL v1 promise; one-shot 0.4 JSON is the initial Business
API; plan lifetime is evidence-driven; UI Automation is a real Headful bridge,
not a hidden layout probe; the release may contain the companion executable
because current requirements now justify it.

Rejected: fixed five-minute lifetime as a permanent contract, a prematurely
stable v1 surface, reflection-based MainForm invocation, hidden/mock GUI
substitution, and future macOS migration as a v0.4 blocker.

## 12. Milestones and gates

- [x] M0a: read governing instructions and live GitHub state
- [x] M0b: create integration worktree from exact PR #70 head
- [x] M0c: isolated baseline bound to `b4308ee`
- [x] M0d: independent architecture review and checkpoint push
- [x] M1: #69 transaction/recovery implementation and fault matrix
- [x] M2: complete shared Application use cases and WinForms migration
- [x] M3: Business Automation host, schema, packaging, and process tests
- [x] M4: GUI manifest, stable IDs, real-control bridge, trace, and safety
- [x] M5: one-command Release Headful E2E and 100% entry/action gate
- [x] M6: version/docs/release notes/migration notes and preview package
- [x] M7: final exact-head local suite, policy-authorized Claude reliability
  challenge, independent local final-diff/GitHub review, CI, and handoff report

Completion is permitted only when every required item passes at the same final
head. Otherwise the handoff states `PARTIAL-BLOCKED` with exact external
blockers; touching real data or a protected boundary is `FAILED`.
