# ExMonty Update Procedure

Run this procedure periodically to pull upstream monty changes and update ExMonty.

## Overview

ExMonty pins a specific monty git revision in `native/ex_monty/Cargo.toml`. Upstream
monty is under active development (~200+ commits since Jan 2025). This procedure walks
through pulling, assessing, and integrating changes.

**Track tagged releases, not `main`.** Upstream cuts `v0.0.x` tags; target the latest tag
unless the user explicitly asks to chase `main` (e.g. for a specific unreleased fix).
The bleeding edge is fine for upstream contributors but exposes us to in-flight refactors.

---

## Phase 1: Pull and Assess

### 1.1 Fetch latest upstream and find the latest tag

```bash
cd ../monty && git fetch origin --tags
git tag --sort=-version:refname | head -5
```

(The monty repo is at `../monty` relative to the ex_monty project root.)

Pick the highest `v0.0.x` tag. That's the **target rev** for this update.

### 1.2 Identify our current pin

```bash
grep 'rev = ' native/ex_monty/Cargo.toml
git describe --tags <OUR_PINNED_REV>   # context: which tag are we at/after?
```

### 1.3 Review changes since our pin

This is the critical step. Focus on the public API surface.

```bash
cd ../monty
git diff <OUR_PINNED_REV>..<TARGET_TAG> -- crates/monty/src/lib.rs
```

This file contains **all** public re-exports. Any change here directly affects us.
Look for:

- **Renamed types or functions** — will cause compile errors
- **Changed signatures** (new params, different return types) — silent breakage risk
- **Removed exports** — will cause compile errors
- **New exports** — opportunities for new features

Then review the implementation files behind our dependencies:

```bash
# Our full API surface — review changes in these modules:
git diff <OUR_PINNED_REV>..<TARGET_TAG> -- \
  crates/monty/src/run.rs \
  crates/monty/src/object.rs \
  crates/monty/src/os.rs \
  crates/monty/src/resource.rs \
  crates/monty/src/exception_public.rs \
  crates/monty/src/exception_private.rs \
  crates/monty/src/io.rs \
  crates/monty/src/run_progress.rs \
  crates/monty/src/fs/dispatch.rs \
  crates/monty/src/fs/path_security.rs \
  crates/monty/src/fs/mount_table.rs
```

For every update, explicitly re-check these security invariants rather than
relying on compilation: `PrintWriterCallback` output must remain fallible and
budgeted by ExMonty; mount matching must be component-aware; host paths must be
canonicalized; symlinks must not escape a mount; read-only and write quotas must
fail closed; `resume_pending` and `ResolveFutures::resume` must preserve call-ID
ownership and retry semantics.

### 1.4 Read the commit log for context

```bash
git log --oneline <OUR_PINNED_REV>..<TARGET_TAG> -- crates/monty/
```

Pay attention to commits that mention: breaking changes, API changes, new features,
bug fixes, or anything related to types we use.

---

## Phase 2: Classify Changes

Sort what you found into categories:

### Breaking (must fix before bump)
Changes to types/functions we directly use:

| We use | Where in our code |
|--------|-------------------|
| `MontyRun::new()`, `.run()`, `.start()`, `.clone()` | `lib.rs`, `interactive.rs` |
| `MontyObject` variants + encoding | `types.rs` (22KB — largest module) |
| `OsFunction` variants | `types.rs`, `interactive.rs` |
| `MontyException`, `ExcType`, `StackFrame` | `error.rs`, `interactive.rs` |
| `FunctionCall::resume()`, `FunctionCall::resume_pending()`, `ResolveFutures::resume()` | `interactive.rs`, `resources.rs` |
| `RunProgress` variants | `interactive.rs` |
| `ExtFunctionResult` variants | `interactive.rs` |
| `PrintWriter`, `PrintWriterCallback` | `lib.rs`, `interactive.rs`, `output.rs` |
| `ResourceLimits`, `LimitedTracker`, `ResourceError` | `lib.rs`, `types.rs`, `resources.rs` |
| `Serialize`/`Deserialize` on `MontyRun`, `Snapshot`, `FutureSnapshot` | `serialization.rs` |

### Non-breaking improvements
- Bug fixes in monty internals (no API change)
- New `MontyObject` variants (we'll hit a match-not-exhaustive warning)
- New `OsFunction` variants (same)
- Performance improvements

### New capabilities
- New public API types/functions we might want to expose

---

## Phase 3: Update

### 3.1 Switch to path dependency for development

Edit `native/ex_monty/Cargo.toml`:

```toml
# Comment out the git dep:
# monty = { git = "https://github.com/pydantic/monty.git", rev = "..." }

# Use local path for iteration:
monty = { path = "../../../monty/crates/monty" }
```

(Assumes `../monty` relative to `ex_monty` project root, i.e. `../../../monty/crates/monty`
relative to the Cargo.toml.)

### 3.2 Build and fix compile errors

```bash
cd native/ex_monty && cargo check 2>&1
```

Common fixes by category:

**Renamed types/functions** — Find-and-replace in our Rust source.

**Changed method signatures** — Update call sites in our NIFs. Check if new
parameters need to be threaded through from Elixir.

**New MontyObject variants** — Add arms to the match in `types.rs:encode_monty_object()`.
Decide on Elixir encoding (tagged tuple, struct, etc.).

**New OsFunction variants** — Add arms to `encode_os_function()` in `types.rs`
and handling in `interactive.rs`.

**New ExcType variants** — Usually handled automatically by `ExcType::from_str()`,
but verify.

**Serialization breakage** — If `MontyRun`, `Snapshot`, or `FutureSnapshot` changed
their serialized format, existing serialized data is incompatible. Consider bumping
a version marker if we persist these.

### 3.3 Update Elixir side if needed

If new types or call patterns were added, update:
- `lib/ex_monty.ex` — main API
- `lib/ex_monty/native.ex` — NIF bindings
- `lib/ex_monty/sandbox.ex` — handler dispatch
- Relevant structs in `lib/ex_monty/`

### 3.4 Run the test suite

```bash
mix test
```

All 90+ tests should pass. If upstream changed behavior (not just API), some
assertions may need updating.

### 3.5 Update documentation

If new progress tags, types, or call signatures were added, update the docs:
- `ExMonty` moduledoc — `@type progress`, "Progress Values" list, interactive examples
- `ExMonty.FunctionCall` / `ExMonty.OsCall` moduledocs — field descriptions, usage context
- `ExMonty.Sandbox` moduledoc — handler dispatch behavior
- Typespec changes on any public function whose signature changed

This is easy to miss — any new tag or struct field that reaches the Elixir side
needs corresponding doc updates.

### 3.6 Add tests for new functionality

If new MontyObject variants or OsFunction variants were added, write tests
exercising them through `ExMonty.eval/2` or the sandbox.

---

## Phase 4: Pin and Ship

### 4.1 Switch back to git dependency

```toml
monty = { git = "https://github.com/pydantic/monty.git", rev = "<NEW_COMMIT_HASH>" }
```

Use the full 40-char hash of the monty commit the **target tag** points at
(`git rev-parse <TARGET_TAG>^{commit}`). Pinning to a hash rather than the tag itself
means the dep can't silently move if a tag is force-updated.

### 4.2 Verify clean build from git dep

```bash
cd native/ex_monty && cargo update -p monty
cd ../.. && mix clean && mix test
```

### 4.3 Update CHANGELOG.md

If the update includes breaking changes, new features, or notable bug fixes,
add an entry to `CHANGELOG.md` under the `[Unreleased]` section summarizing
what changed from the ExMonty user's perspective.

### 4.4 Commit

```
Update monty to <short-hash>

- <summary of breaking changes fixed>
- <summary of new features added>
- <summary of upstream improvements picked up>
```

---

## Quick Reference: One-liner Diff Check

To quickly check if there are API changes worth pulling, against the latest tag:

```bash
cd ../monty && git fetch origin --tags && \
  LATEST_TAG=$(git tag --sort=-version:refname | head -1) && \
  git diff $(grep -oP 'rev = "\K[^"]+' ../ex_monty/native/ex_monty/Cargo.toml)..$LATEST_TAG \
    -- crates/monty/src/lib.rs
```

If this diff is empty, there are no public API changes — only internal improvements.
Still worth updating periodically for bug fixes.

---

## When to Update

- **When upstream cuts a new tag** (`v0.0.x`) — that's the natural cadence
- **Immediately** if upstream fixes a bug affecting us (then either chase `main` or wait
  for the next tag, depending on severity — ask the user)
- **Before any ExMonty release** to pick up the latest stable tag
- **When a new MontyObject/OsFunction variant** is needed for a feature we want
