Create a Persistra project¶
The initializer creates a standard non-packaged uv research application. Install Persistra, then supply an explicit target directory:
persistra init example-project
cd example-project
uv sync
uv run python main.py
uv run persistra project validate .
uv run persistra inspect .
Use --name NAME when the distribution name should differ from the directory name. Persistra
strips surrounding whitespace and normalizes periods, underscores, and repeated hyphens to one
lowercase hyphen. It prints the normalized name and absolute project root.
Initialization does not prompt, access the network, run Git or uv, create an environment, or
resolve dependencies. The target must be absent or an existing empty directory. Its parent must
exist. Persistra refuses symlink targets, nonempty directories, files, and path collisions. A
failed or cancelled initialization closes an opened store and then attempts to remove every path
created by that invocation. Database creation uses a private staging directory, so partial main
files and database sidecars are part of the same rollback. KeyboardInterrupt retains its
original cancellation semantics; the CLI prints persistra: cancelled without a traceback and
returns status 130.
Generated projects declare persistra[inspect] because their standard README includes the local
browser-inspection workflow. This extra includes visualization support. Projects that do not use
the inspector can deliberately replace it with base persistra or persistra[viz].
Rollback identifies created paths by their filesystem device and inode. It preserves paths that
existed before initialization, untracked paths added concurrently, and replacements moved into a
tracked location. Those concurrent paths can leave an otherwise empty target directory behind.
Cleanup failures are supplemental cancellation notes and do not replace the original
KeyboardInterrupt or SystemExit. These guarantees apply to failures delivered to the running
process. They cannot recover from SIGKILL, power loss, or filesystem corruption.
When Persistra is installed from a local directory, the initializer adds an absolute
tool.uv.sources path for Persistra to the generated pyproject.toml. It also retains editable
installation status. This mapping makes uv sync use the same checkout instead of querying a
package index. Registry installations do not add the mapping. Update or remove the absolute path
if you move the checkout or share the generated project with another machine.
Standard layout¶
Format version 1 creates this fixed tree:
example-project/
├── .gitignore
├── .python-version
├── README.md
├── persistra.toml
├── pyproject.toml
├── main.py
├── data.duckdb
├── cache/
│ └── responses/
├── artifacts/
│ ├── research/
│ └── trading-engine/
├── notebooks/
└── tests/
└── test_project.py
Marker files retain intentionally empty standard directories in Git. The generated application
has no build backend or package directory. uv creates uv.lock and .venv only when you run
uv sync. A project initialized from an editable local checkout includes this source mapping:
A noneditable local installation omits editable = true.
data.duckdb is an initialized DuckDBStore. Additional root-level *.duckdb files are also
available to the local inspector without recursive discovery.
Diagnose a project without changing it¶
Validate exactly the directory you provide:
The command does not search the current directory or any parent, and it does not create, repair,
migrate, or install anything. It checks the strict persistra.toml identity, containment and file
types for the fixed layout, unsafe symlinks, the primary store's complete integrity, and
pyproject.toml syntax and Persistra dependency declaration when that file exists.
Human output lists ordered findings as severity: code [location]: message and ends with error and
warning counts. JSON output has validation_version = 1, the absolute root, project name when the
manifest is valid, validity and counts, and the same ordered findings. Repeated validation of
unchanged inputs produces the same findings and ordering.
The command returns status 0 when there are no errors, including when warnings are present. It returns status 1 when validation reports one or more errors. Command-line syntax and unexpected operational failures return status 2; cancellation returns 130.
Diagnostic categories¶
| Codes | Severity | Meaning |
|---|---|---|
project.root.missing, project.root.unreadable, project.root.symlink, project.root.type |
Error | The explicit root cannot safely identify a directory. |
project.path.outside_root, project.path.unreadable, project.path.symlink, project.path.type |
Error | A standard path escapes the root, cannot be inspected, is a symlink, or has the wrong type. |
project.path.missing |
Warning or error | Missing optional standard files and directories warn; a missing manifest or primary store errors. |
project.manifest.unreadable, project.manifest.malformed, project.manifest.schema, project.manifest.version_unsupported |
Error | The identity manifest cannot be read or does not meet the strict supported contract. |
project.pyproject.unreadable, project.pyproject.malformed |
Error | A present environment manifest cannot be read or parsed. |
project.pyproject.dependency |
Warning | A present pyproject.toml lacks a valid dependency list or Persistra declaration. |
store.* |
Error | The primary store verifier found one of the integrity conditions documented in Store and query results. |
Warnings cover resources that may be intentionally absent without invalidating project identity, including standard runtime directories, helper files, and dependency declarations. Wrong types, unsafe links, malformed present files, missing identity or store resources, and store corruption remain errors.
Use paths explicitly¶
Open the project from a path your application owns. Existing Persistra APIs do not search the current directory or parent directories:
from pathlib import Path
from persistra.data import FredClient
from persistra.project import PersistraProject
project = PersistraProject.open(Path(__file__).resolve().parent)
client = FredClient.from_env(
cache_directory=project.raw_cache_directory,
)
Use these fixed paths for format version 1:
| Path property | Purpose |
|---|---|
store_path |
Primary normalized DuckDBStore at data.duckdb |
raw_cache_directory |
Raw provider responses passed explicitly to clients |
research_artifact_directory |
Research manifests and caller-selected research output |
trading_engine_artifact_directory |
Caller-managed Trading Engine scenarios and journals |
notebook_directory |
Caller-owned notebooks |
Use a run-specific child of trading_engine_artifact_directory when writing scenarios and
retaining journals. Persistra does not route caches, stores, or artifacts from the process working
directory.
Understand the project manifest¶
persistra.toml identifies only the project and fixed layout:
Version 1 accepts exactly those fields. It does not allow path overrides. Missing, unknown, mistyped, malformed, and unsupported fields fail with an actionable error.
The project manifest does not record datasets, parameters, environments, executions, or hashes.
ResearchManifest and caller-owned Trading Engine provenance retain those separate
responsibilities. Raw cache entries, normalized stores, research manifests, and replay artifacts
never share one representation.
Choose retention and version-control policies¶
The generated .gitignore excludes raw response contents, DuckDB files, generated research and
Trading Engine outputs, Python and tool caches, notebook checkpoints, .env files, and local
secret keys. It retains directory markers and does not ignore uv.lock, project manifests,
source, tests, README files, or notebooks.
Generated data and artifacts are ignored by default, not backed up. You own their retention, backup, access-control, and deletion policies. Commit selected reproducibility manifests deliberately after checking them for credentials and sensitive data. Put replay or research inputs that should be versioned outside ignored generated-output directories.
The raw response cache supports offline replay but does not replace normalized storage. The primary store does not replace research manifests. Trading Engine artifacts remain separate from both. This separation keeps provenance and retention decisions explicit.