# Package Format v2 — Task List

**Design:** `docs/PACKAGE_FORMAT_V2_DESIGN.md`
**Target:** Coral v0.3
**Date:** 2026-03-07
**Requires:** Reef >= 0.5.0

---

## Phase 1: mtree module and manifest generation — COMPLETE

- [x] Create `src/util/mtree.reef` module with `ManifestEntry` type
- [x] Implement `parse_manifest()` — line-oriented parser, split on space, extract key=value pairs
- [x] Implement `generate_manifest()` — walk directory tree using `fs.stat`, `fs.perm`, `fs.link.readlink()`, `checksum.sha256_file()`
- [x] Implement accessor functions: `entry_type`, `entry_path`, `entry_mode`, `entry_sha256`, `entry_link`, `entry_uname`, `entry_gname`
- [x] Handle sort order: lexicographic by path so dirs precede their contents
- [x] Skip metadata files (`.PKGINFO`, `.MANIFEST`, `.CONFIG`, `.SCRIPTS/`) during generation
- [x] Update `build.reef`: replace `generate_footprint()` with `generate_manifest()` using new mtree module
- [x] Rename output file from `.FOOTPRINT` to `.MANIFEST` in build output
- [ ] Test: build a package, verify `.MANIFEST` output contains correct types, modes, ownership, checksums
- [ ] Test: parse a generated manifest back and verify round-trip correctness

## Phase 2: fs.ops-based installer — COMPLETE

- [x] Rewrite `install_files()` in `package.reef` to parse `.MANIFEST` instead of `.FOOTPRINT`
- [x] Implement dir install: `dir.create_dir_all()`, `fs.perm.chmod()`, `fs.perm.chown()`
- [x] Implement file install: `fs.ops.copy_file_preserve()`, `fs.perm.chmod()`, `fs.perm.chown()`
- [x] Implement symlink install: remove conflicting target, `fs.link.symlink()`
- [x] Add post-copy sha256 verification against manifest checksum
- [x] Integrate config file checksum logic (see Phase 2b below)
- [x] Remove rsync call from `install_files()`
- [x] Remove symlink conflict prep_cmd shell-out (handle inline during manifest walk)
- [x] Error handling: stop on any failed file operation
- [x] Update `read_footprint()` to read `.MANIFEST` first, fall back to `.FOOTPRINT`
- [x] Update `register_from_extract_dir()` to store `manifest.mtree` in database
- [x] Update `remove_files()` to use `fs.ops.remove_file` instead of shell-outs
- [x] Add `remove_files_manifest()` for manifest-driven removal with explicit directory tracking
- [x] Clean up stale rsync comments in `install.reef`
- [ ] Test: install a package to a test root, verify all files/dirs/symlinks match manifest
- [ ] Test: install package with setuid binary (mode=4755), verify mode preserved
- [ ] Test: install package with symlinks, verify targets correct

### Phase 2b: Config file checksum logic — COMPLETE

- [x] Update `install_config_file()` to accept manifest sha256 and stored sha256 parameters
- [x] On fresh install: copy file normally
- [x] On upgrade (file exists, matches old checksum): overwrite with new version
- [x] On upgrade (file exists, differs from old checksum): install as `.pkg-new`, preserve user's file
- [x] Remove old content-comparison logic (`str.equals(src_content, dest_content)`)
- [x] On remove: skip config files where installed sha256 differs from package sha256
- [ ] Test: fresh install places config file and records checksum
- [ ] Test: upgrade with unmodified config overwrites cleanly
- [ ] Test: upgrade with user-modified config installs `.pkg-new` and preserves original

## Phase 3: Enhanced .PKGINFO — COMPLETE

- [x] Add `runtime_deps`, `build_deps`, `runtime_deps_count`, `build_deps_count` fields to `PackageInfo` type in `types.reef`
- [x] Update `new_package_info()` factory to initialize new fields
- [x] Update `generate_pkginfo()` in `build.reef` to emit `[dependencies]` section
- [x] Write `runtime` array from `ctx.port.deps.runtime`
- [x] Write `build` array from `ctx.port.deps.build`
- [x] Skip `[dependencies]` section if both arrays are empty
- [x] Update `read_pkginfo()` in `package.reef` to parse `[dependencies]` section
- [x] Handle legacy packages without `[dependencies]` gracefully
- [x] Add `parse_toml_array()` helper to `package.reef`
- [x] Handle version constraint syntax in dependency strings (`>=`, `<=`, `>`, `<`, `=`)
- [x] Add version comparison utility for constraint checking
- [ ] Test: build a package with dependencies, verify `.PKGINFO` contains correct `[dependencies]`
- [ ] Test: parse `.PKGINFO` with dependencies and verify fields populated

## Phase 4: Script model cleanup — COMPLETE

- [x] Remove `.reef` script type from `run_script()` in `package.reef`
- [x] Remove `.reef` script type from `run_stored_script()` in `package.reef`
- [x] Change `.sh` invocation from `sh` to `zsh` in `run_script()`
- [x] Change `.sh` invocation from `sh` to `zsh` in `run_stored_script()`
- [x] Update script search order: try `.sh` first, then bare executable
- [x] Add `pre-upgrade.sh` / `post-upgrade.sh` hook support
- [x] Pass `$VERSION` argument to pre-install, post-install, pre-remove, post-remove
- [x] Pass `$OLD_VERSION $NEW_VERSION` arguments to pre-upgrade, post-upgrade
- [x] Abort install/remove/upgrade if pre-* hook exits non-zero
- [x] Log warning (don't abort) if post-* hook exits non-zero
- [x] Store upgrade scripts in database alongside remove scripts at install time
- [x] Implement upgrade flow: pre-upgrade → remove old files → install new files → post-upgrade
- [ ] Test: package with pre-install that exits 0 — install proceeds
- [ ] Test: package with pre-install that exits 1 — install aborts
- [ ] Test: package with post-install that exits 1 — install completes with warning
- [ ] Test: upgrade with pre-upgrade/post-upgrade scripts, verify version args passed

## Phase 5: Removal and verify — COMPLETE

- [x] Rewrite `remove_files()` to use `fs.ops` instead of shell-outs (done in Phase 2)
- [x] Add `remove_files_manifest()` with explicit directory tracking (done in Phase 2)
- [x] Respect config file protection on remove (skip user-modified configs) (done in Phase 2b)
- [x] Implement `coral verify <pkg>` command
  - [x] Read stored manifest from database
  - [x] For each file entry: check exists, compare sha256, compare mode/ownership
  - [x] For each symlink entry: check exists, verify link target
  - [x] For each dir entry: check exists
  - [x] Report: missing files, checksum mismatches, permission changes, broken symlinks
- [ ] Test: remove a package, verify all files deleted and empty dirs cleaned up
- [ ] Test: remove a package with user-modified config, verify config preserved
- [ ] Test: verify a clean install — all files pass
- [ ] Test: verify after modifying a file — checksum mismatch reported

---

## Cleanup

- [x] Remove `generate_footprint()` from `build.reef` (replaced by `generate_manifest()`)
- [x] Update `read_footprint()` to read `.MANIFEST` via mtree parser (with `.FOOTPRINT` fallback)
- [x] Update `verify_package()` to check for `.MANIFEST` or `.FOOTPRINT`
- [x] Remove rsync from install path (only remains in `repo.reef` mirror command)
- [x] Clean up rsync comments in `install.reef`
- [x] Update `docs/GUIDE.md` — fix .reef references, sh→zsh, .pkg→.pkg-new, add verify command, add version constraints, add upgrade scripts
