# SaC Applied State

`univer sac apply <univerfile>` validates the sidecar source chain, validates target-owned active applied state, checks target unit state drift, bundles one pending pack, applies it to the target univerfile through Facade migration code, commits it, and then moves to the next pack.

Apply acquires the target SaC mutation lock before reading source chain or active applied state. The lock state is stored in the target univerfile storage. A lock conflict means another SaC mutation is active for the same target or a previous process crashed before releasing its lock.

Every successfully applied spreadsheet pack persists its runtime action batch per affected unit: created units are added, deleted units are soft-deleted, and mutated units are committed through workbook versioning as local changesets on the target univerfile. To undo the newest applied SaC pack, use `univer sac rollback <univerfile>`.

## Target Lifecycle

Apply reads source from the resolved hidden sidecar `migrations/` directory. There is no `--target` option, no `sac.config.ts` target binding, and no `univer-workspace.json` include registry.

Each unit migration runs against the same target univerfile. Use Facade APIs in `apply({ univerAPI })` to create or modify the needed spreadsheet state when those APIs are present in local generated types:

```ts
export default defineUnitMigration({
  apply({ univerAPI }) {
    const localUnitId = "replace-with-discovered-sheet-localUnitId";
    const workbook = univerAPI.getWorkbook(localUnitId);
    const sheet = workbook.getSheetByName("Revenue") ?? workbook.create("Revenue", 1000, 26);
    sheet.getRange("A1").setValue("Revenue");
  }
});
```

Apply executes migration source in the same initialized Univer runtime. Generated and hand-authored source should use only Facade APIs present in the local sidecar `types/`; source generation support is tracked separately from runtime API availability.

Current apply runtime supports spreadsheet Facade execution only. The target `.univer` may still
contain doc, slide, or unknown units; those units remain visible as capability-limited units and are
not treated as spreadsheets. Document and slide unit execution will use the same
`apply({ univerAPI })` wrapper shape when their runtimes exist.

## Active Applied State

Each applied pack writes one target-owned active state entry with pack id, description, source hash, previous pack pointer, unit changes, runner metadata, timestamp, and origin. The expected unit state is derived by folding `unitChanges` from the materialize baseline through the active chain.

After a pack has been recorded in the target active state, its `pack.ts`, imported migration source files, assertions, and referenced data assets are immutable while it remains active. Do not edit, rename, reorder, or delete active source; create a new follow-up pack for later workbook behavior changes, or roll back the current tail pack first.

## Rollback

`univer sac rollback <univerfile>` reverses exactly the last applied migration pack. It uses target-owned unit change metadata rather than running migration source in reverse:

Rollback also acquires the target SaC mutation lock before reading the active chain tail. After the storage-backed lock is acquired, rollback still relies on storage transaction semantics for inverse unit operations and active state truncation.

- units added by the tail pack are hard deleted
- units soft deleted by the tail pack are restored
- units changed by the tail pack are reset to their before revision

After rollback, the active applied chain is truncated by one entry. The source pack remains in the resolved hidden sidecar `migrations/` directory and becomes pending again, so it can be edited and applied later. Rollback cannot remove the materialize baseline.

Apply fails before executing pending source when:

- the target univerfile active applied state is missing or malformed
- the active applied chain is ahead of sidecar source
- an already-applied pack id has a different source hash
- target unit state has drifted since the active chain head
- the current target univerfile contains unit state unsupported by the active SaC runtime
- the runtime action batch contains no persistent workbook actions
- a mutation commit fails for an affected workbook unit

If every source pack is already active with matching hashes and the target unit state has not drifted, apply succeeds as an up to date no-op.

## Verification

Apply success is not the Spreadsheet TDD completion gate. For assertion-backed SaC work, run `univer sac verify <univerfile> --json` after apply and read the `reportPath` from the JSON summary.

Use SaC README, sidecar-scoped readonly `inspect`, and exported handoff files as supplemental workbook-visible checks when they help explain or hand off the result:

```bash
univer sac verify ./Budget.univer --json
printf '%s' '{}' | univer inspect ./Budget.univer --tool units --params -
univer export ./Budget.univer ./Budget.xlsx
```

Do not treat successful command output as the only validation for a non-trivial workbook change.
