> ## Documentation Index
> Fetch the complete documentation index at: https://docs.canary.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Package a program

> Distribute a rater with its prepare function, schema, and regression examples in one versioned program bundle.

Package a `prepare` function, its schema and its examples with a rater, so one bundle carries
the whole program.

[Install Relativities and download the example data](/relativities/setup) before running this page.

```python theme={null}
from pathlib import Path

from relativities import Program, Rater

samples = Path("relativities-examples/ppa")
with Rater.load(samples / "carrier-a.zip") as engine:
    packed = Program.pack(
        engine, samples / "program_a", "carrier-a-program.zip",
        prepare="program:prepare", schema="program:SCHEMA", examples="examples",
    )
packed.check()[["submission_id", "status", "expected_premium", "premium", "matches"]]
#   submission_id          status  expected_premium  premium  matches
# 0            S2       succeeded             825.0    825.0     True
# 1            S3       succeeded             375.0    375.0     True
# 2            S4  invalid_inputs               NaN      NaN     True
packed.close()
```

[Bundles and exact rating](/relativities/explanation/bundles-and-exact-rating) describes how a bundle carries
and verifies its program.

```mermaid theme={null}
flowchart LR
  engine["Rater bundle"] --> pack["Program.pack"]
  folder["Program folder: prepare, schema and examples"] --> pack
  pack --> bundle["Bundle with the program: new bundle_id"]
  bundle --> load["Program.load"]
  load --> check["program.check: rate the examples"]
```

## Lay out the program folder

Put the program module, its helpers and the examples in one folder.

```text theme={null}
program_a/
  program.py          # prepare(book), and SCHEMA: the book the program accepts
  common.py           # a helper that program.py imports as "from .common import ages"
  examples/
    book/             # the Book.read_csv layout
    expected.csv
```

* Import each helper relatively, as `from .common import ages`.
* In `program.py`, define `prepare(book)`, which returns each submission's rater input by
  `submission_id`.
* Also define `SCHEMA`, such as `SCHEMA = ppa.SCHEMA`. Use `with_columns` to declare a column
  of your own that `prepare` reads.
* Put a small book in `examples/book`, in the `Book.read_csv` layout.

## Write the expected outcomes

Write one row per example submission in `expected.csv`.

```text theme={null}
submission_id,status,premium
S2,succeeded,825.00
S3,succeeded,375.00
S4,invalid_inputs,
```

1. Write the `submission_id` and `status` columns.
2. Add the rater's outcome columns that you want to check, such as `premium`.
3. For a submission that stays unrated, leave its outcome cells empty.

## Pack the program

Call [`Program.pack`](/relativities/reference/relativities#program) with the rater, the folder and a new `.zip`
path.

* Name `prepare` and the schema as `"module:name"`, such as `"program:prepare"` and
  `"program:SCHEMA"`.
* To choose the result columns, name a `read` function the same way, such as `"program:read"`.
* To add examples, name their folder, such as `"examples"`.

`Program.pack` writes the bundle, loads it and checks its examples. The bundle gets a new
`bundle_id`, and the rater's own ID stays as `engine_id`.

## Load the program

To load a packaged program, call `Program.load` with a `Path` or a catalog ID.

```python theme={null}
program = Program.load(Path("carrier-a-program.zip"))
program.expected.columns.tolist()  # ['submission_id', 'status', 'premium']
program.examples  # Book(3 households, 3 submissions, 4 drivers, 4 vehicles)
```

`Program.load` takes `revision=` and `local_files_only=` as `Rater.load` does. `program.examples`
holds the example book, and `program.expected` holds the expected table.

<Warning>
  Loading a program runs its Python. Load programs from your own catalog or from files you trust.
</Warning>

## Check the examples

Call `program.check()` to rate the examples again.

```python theme={null}
program.check()["matches"].all()  # True
```

The table puts each expected value beside the rated one. Numbers match within half a cent. A
mismatch raises `CheckError`, and its `comparison` holds the table.

## Carry the program to a rebuilt rater

To move the program to a rater rebuilt from edited RSL, call `program.with_rater`.

```python theme={null}
import rslc
from rslc.bundle import export_deployment_bundle

source = Path("carrier-a-400")
source.mkdir()
(source / "rater.rsl").write_text((samples / "carrier-a.rsl").read_text().replace("300 *", "400 *"))
rslc.emit_python(str(source / "rater.rsl"), allow_holes=False)["ok"]  # True
export_deployment_bundle(source, Path("carrier-a-400.zip"))
rebuilt = program.with_rater(Rater.load(Path("carrier-a-400.zip")))
rebuilt.expected["premium"].tolist()  # [1050.0, 475.0, nan]
```

This rebuilt rater has a liability base rate of 400. The examples keep their statuses, so `with_rater` returns the program with the new outcomes as expected values.
A status change raises `CheckError` with the submission ID.

`with_rater` refreshes the expected premiums from the replacement rater. Those refreshed values need independent validation against the intended change.
Use [Validate a program](/relativities/how-to/validate-a-program) before accepting them as regression expectations.

## Close the programs

Close each program to close its rater.

```python theme={null}
program.close()
rebuilt.close()
```

`with Program.load(...) as program:` closes the program at the end of the block.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.