## upgrade.Plan


The actions a command would perform, plus the inputs they depend on.


Usage

``` python
upgrade.Plan(
    command,
    actions=list(),
    context=dict(),
    notes=list(),
    preconditions=list(),
    created_at=(lambda: datetime.now(UTC).isoformat(timespec="seconds"))(),
    tether_version=_running_version(),
    saved_digest=None
)
```


## Parameter Attributes


`command: str`  
[commit](vcs.VcsAdapter.md#tether.vcs.VcsAdapter.commit), [new](vcs.VcsAdapter.md#tether.vcs.VcsAdapter.new), `gc`, [promote](testing.ObjectBackend.md#tether.testing.ObjectBackend.promote), `import`, `publish`, or `repair`.

`actions: list[Action] = list()`  
Store-writing steps, in execution order.

`context: dict[str, Any] = dict()`  
Inputs the plan was computed from (states, manifest hash, message, …); apply re-validates these before writing.

`notes: list[str] = list()`  
Non-actions worth showing (objects skipped and why).

`preconditions: list[Precondition] = list()`  
What must still hold at apply time; see [Precondition](Precondition.md#tether.Precondition).

`created_at: str = (lambda: datetime.now(UTC).isoformat(timespec=``"seconds"))()`  

`tether_version: str = _running_version()`  
The tether version that made the plan. A saved plan loads only under that same version (see [from_dict()](Plan.from_dict.md#tether.Plan.from_dict)): what the preconditions check, and how apply reads its actions, are that version's.

`saved_digest: str | None = None`  
The [digest](Plan.digest.md#tether.Plan.digest) a loaded plan was saved with (`None`: made in this process); `Repo._verify_plan` refuses a plan whose content no longer matches it.


## Attributes

| Name | Description |
|----|----|
| [NON_WRITES](#NON_WRITES) | Actions that write nothing to an external system when applied. |
| [writes](#writes) | Actions that touch an external system or the working tree. |

------------------------------------------------------------------------


#### NON_WRITES


Actions that write nothing to an external system when applied.


`NON_WRITES=frozenset(`\
`    {`\
`        `<span class="st">`"trunk",`\
`        ``"keep-branch"``,`\
`        ``"keep-pin"``,`\
`        ``"keep-store"``,`\
`        ``"defer-fork"``,`\
`        ``"refuse"``,`\
`        ``"reuse"``,`\
`        ``"adopt"``,`\
`        ``"hold"``,`\
`        ``"share"``,`\
`    }`\
`)`</span>


------------------------------------------------------------------------


#### writes


Actions that touch an external system or the working tree.


`writes: list[Action]`


## Methods

| Name | Description |
|----|----|
| [digest()](#digest) | SHA-256 of everything else in the saved plan, as JSON reads it back |
| [edited()](#edited) | Whether this plan was loaded and its content no longer matches the |
| [from_dict()](#from_dict) | Load a saved plan. |
| [missing_preconditions()](#missing_preconditions) | What this plan lacks of `REQUIRED_PRECONDITIONS` and |
| [render()](#render) | Human-readable lines, one per action, then notes. |
| [require()](#require) | Append a precondition (see [Precondition](Precondition.md#tether.Precondition)). |

------------------------------------------------------------------------


#### digest()


SHA-256 of everything else in the saved plan, as JSON reads it back


Usage

``` python
digest()
```


(keys sorted, values through `str` where JSON has no type).

The preconditions vouch for the world the actions and context were computed from; the digest binds the three together, so a saved plan edited in one place - an action dropped, a `target_state` or `bookmark_commit` changed - is refused at apply rather than applied under checks that describe another plan. It detects edits, it does not authenticate: whoever can write the file can recompute it.


------------------------------------------------------------------------


#### edited()


Whether this plan was loaded and its content no longer matches the


Usage

``` python
edited()
```


digest it was saved with (see [digest()](Plan.digest.md#tether.Plan.digest)).


------------------------------------------------------------------------


#### from_dict()


Load a saved plan.


Usage

``` python
from_dict(data)
```


##### Raises


`StalePlanError`  
Another tether version made the plan, or it records none (saved before plans recorded it).

`ConfigError`  
The plan's format is not `PLAN_FORMAT`.


------------------------------------------------------------------------


#### missing_preconditions()


What this plan lacks of `REQUIRED_PRECONDITIONS` and


Usage

``` python
missing_preconditions()
```


`REQUIRED_ACTION_PRECONDITIONS`, as [kind](Action.md#tether.Action.kind) or `kind for key` entries; empty when it carries everything its command requires.


------------------------------------------------------------------------


#### render()


Human-readable lines, one per action, then notes.


Usage

``` python
render()
```


------------------------------------------------------------------------


#### require()


Append a precondition (see [Precondition](Precondition.md#tether.Precondition)).


Usage

``` python
require(kind, expected=None, *, key=None, detail="", **params)
```
