## experimental.lifecycle


[experimental.lifecycle](experimental.lifecycle.md#tether.experimental.lifecycle)


Usage


Store lifecycle: create a store tether then owns, and reclaim it.


**Experimental.** Everything else tether does to a store is recoverable from the manifests: a deleted pin can be `repair`ed, a deleted branch re-forked. `delete-store` cannot be, and its blast radius is a whole store. The feature has also not yet run against the resources it was written for - an S3 prefix, a second clone - outside tests with stand-ins. So it lives here, behind the same seam as the registry: the stable surface is what users type and import (`tether add --create`, `tether gc --delete-stores`, `Repo.create`, `Repo.add(create=True)`), and the engine half is imported lazily by those entry points. Module paths under `tether.experimental` are not API.

What is here:

- the created-store index beside the repository lock (`tether-created.jsonl`) and the reader of the touched-store journal (`tether-touched.jsonl`). The journal itself - what this clone has forked a branch in or pinned - is core-owned and written by every fork and pin ([tether.oplog.append_touched](oplog.append_touched.md#tether.oplog.append_touched)), so that stable commands never import this module and the record is complete by the time the feature graduates; only the consumer lives here;
- [create_store](experimental.lifecycle.md#tether.experimental.lifecycle.create_store): the `add --create` path (make, mark, index, register, fork on a bookmark), and [undo_add_store](experimental.lifecycle.md#tether.experimental.lifecycle.undo_add_store), its reversal while the store is still empty;
- the `gc --delete-stores` step ([plan_stores](experimental.lifecycle.md#tether.experimental.lifecycle.plan_stores), [apply_store_action](experimental.lifecycle.md#tether.experimental.lifecycle.apply_store_action)): release this dataset's dead refs in stores no manifest names, and delete a created store once the backend confirms nothing else remains, under the rules documented on `plan_created_stores` and `plan_store_refs`.

The backend contract this rides on ([Capability.CREATE](testing.Capability.md#tether.testing.Capability.CREATE): [create](testing.ObjectBackend.md#tether.testing.ObjectBackend.create), [owner](testing.ObjectBackend.md#tether.testing.ObjectBackend.owner), [is_ref_empty](testing.ObjectBackend.md#tether.testing.ObjectBackend.is_ref_empty), [delete_store](testing.ObjectBackend.md#tether.testing.ObjectBackend.delete_store)) stays in `tether.backends.base` with conformance coverage; it is a per-backend fact, marked experimental in its docstring.

**Graduation criteria** - what has to be true before this moves back into `tether.repo` and loses the experimental note:

1.  The S3 arm of Icechunk's [delete_store](testing.ObjectBackend.md#tether.testing.ObjectBackend.delete_store) has run once against a real bucket (the in-memory obstore test covers the logic, not the service).
2.  The two-clone scenario in `tests/test_store_lifecycle.py` has run against a real remote (a hosted git or jj remote), not a local clone.
3.  One release cycle with the feature in users' hands and no data-loss report.
4.  A second Forkable backend declares [CREATE](testing.Capability.md#tether.testing.Capability.CREATE) (Neon project or branch; Lance once an empty schema is acceptable), showing the capability generalises beyond "make an empty directory or repository".
5.  A decision on the touched index: keep it as the mechanism for dead refs in unnamed stores, or fold that into `--prune-bookmarks` by walking the stores of every manifest in history.


## Attributes

| Name | Description |
|----|----|
| [CREATED_FILENAME](#CREATED_FILENAME) | Repository-wide index of stores tether *created*, kept next to the |
| [STORE_OPS](#STORE_OPS) | The plan verbs this module applies ([apply_store_action](experimental.lifecycle.md#tether.experimental.lifecycle.apply_store_action)). |

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


#### CREATED_FILENAME


Repository-wide index of stores tether *created*, kept next to the


`CREATED_FILENAME=``"tether-created.jsonl"`


repository lock in the shared VCS store ([VcsAdapter.shared_dir()](vcs.VcsAdapter.md#tether.vcs.VcsAdapter.shared_dir)): one clone of the dataset, every checkout, never committed. History cannot hold this - an abandoned fork takes the manifest that named the store with it - and a workspace's own log dies with `forget-workspace`. This survives both.


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


#### STORE_OPS


The plan verbs this module applies ([apply_store_action](experimental.lifecycle.md#tether.experimental.lifecycle.apply_store_action)).


`STORE_OPS=frozenset({``"keep-store", `<span class="st">`"forget-store"``, ``"forget-touched"``, ``"delete-store"``})`</span>


## Classes

| Name | Description |
|----|----|
| [CreatedStore](#CreatedStore) | One store tether made (`add --create`); `gc` may remove it once nothing |

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


#### CreatedStore


One store tether made (`add --create`); `gc` may remove it once nothing


Usage

``` python
CreatedStore(
    dataset_id,
    kind,
    identity,
    locator,
    key,
    at,
    bookmark=None,
)
```


references it and the store confirms the owner marker.


##### Parameter Attributes


`dataset_id: str`  

`kind: str`  

`identity: dict[str, Any]`  
`ObjectBackend.identity(locator)`: what a manifest reference is matched on.

`locator: dict[str, Any]`  

`key: str`  
The key it was registered under at creation (informational).

`at: str`  

`bookmark: str | None = None`  
The bookmark the store was created on. Its `tether.ws.*` branch is the one this clone can account for when `gc` judges the store; a same-dataset branch of a bookmark this clone never had may be another actor's.


## Functions

| Name | Description |
|----|----|
| [append_created()](#append_created) | Record a store tether just created (journal-style, synced). |
| [apply_store_action()](#apply_store_action) | Apply one store verb of a gc plan (called from `apply_gc`, after every |
| [create_store()](#create_store) | The `add --create` path, under the caller's writer lock: require |
| [created_stores()](#created_stores) | Stores this dataset created (`add --create`) and has not yet removed. |
| [plan_stores()](#plan_stores) | The `gc --delete-stores` step: created stores first (they may be |
| [preview_store_action()](#preview_store_action) | What a dry run reports for one store verb. |
| [read_created()](#read_created) | Every created store on record (optionally one dataset's), oldest first. |
| [remove_created()](#remove_created) | Forget a store (deleted by `gc`, or by `undo add`). |
| [touched_stores()](#touched_stores) | Stores this clone has forked or pinned in. |
| [undo_add_store()](#undo_add_store) | `add --create` made a store: take it away again while it is still |

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


#### append_created()


Record a store tether just created (journal-style, synced).


Usage

``` python
append_created(shared_dir, entry)
```


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


#### apply_store_action()


Apply one store verb of a gc plan (called from `apply_gc`, after every


Usage

``` python
apply_store_action(repo, a, report, errors, op)
```


other action; `delete-store` re-checks owner and emptiness first).


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


#### create_store()


The `add --create` path, under the caller's writer lock: require


Usage

``` python
create_store(repo, key, kind, locator, *, policy, pre)
```


[CREATE](testing.Capability.md#tether.testing.Capability.CREATE), journal, have the backend make the store with this dataset's owner marker, index it, register it with `origin = "created"`, and on a non-trunk bookmark fork its working branch at once.


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


#### created_stores()


Stores this dataset created (`add --create`) and has not yet removed.


Usage

``` python
created_stores(repo)
```


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


#### plan_stores()


The `gc --delete-stores` step: created stores first (they may be


Usage

``` python
plan_stores(
    repo,
    plan,
    manifests,
    *,
    keep_bookmarks,
    prune_bookmarks,
    force,
    claimed=()
)
```


deleted), then this clone's dead refs in every other store it touched.


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


#### preview_store_action()


What a dry run reports for one store verb.


Usage

``` python
preview_store_action(a, report)
```


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


#### read_created()


Every created store on record (optionally one dataset's), oldest first.


Usage

``` python
read_created(shared_dir, dataset_id=None)
```


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


#### remove_created()


Forget a store (deleted by `gc`, or by `undo add`).


Usage

``` python
remove_created(shared_dir, dataset_id, identity)
```


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


#### touched_stores()


Stores this clone has forked or pinned in.


Usage

``` python
touched_stores(repo)
```


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


#### undo_add_store()


`add --create` made a store: take it away again while it is still


Usage

``` python
undo_add_store(repo, entry, report)
```


empty (its own working branch, if [add](testing.ObjectDiff.md#tether.testing.ObjectDiff.add) forked one, does not count); a store that has been written to stays, and `gc` decides later.
