Files
buzz/desktop/src-tauri
Matt Toohey 602e11b605 fix(desktop): embed the Project Canvas seed template in the binary
Seeding a project's first canvas read `resources/project-canvas-template`
from disk at activation time, resolved from the running executable's
directory in dev builds. That directory is the cargo target directory —
shared across worktrees, rewritten by sibling builds, and deleted wholesale
by toolchain cleanups. When it disappeared under a running app the
first-ever activation of `buzz-bugs` failed with `inspect project canvas
path: No such file or directory (os error 2)` and could not self-heal.

Make the seed bytes part of the binary and drop the on-disk copy, so no
runtime lookup exists to go stale. This follows `resources/pocket-voices`,
which already embeds 7.6 MB of `.wav` through `include_bytes!` and is
deliberately absent from `bundle.resources`. The template is consumed only
as seed bytes: agents author canvases from `CANVASES/index.json` →
`sourcePath`, never from the template, so the disk copy had no remaining
consumer.

- New `project_canvas_package/template.rs` embeds the tree with
  `include_dir!` and walks it into a path-keyed map, running every key
  through `validate_relative_path` and skipping `.DS_Store` exactly as the
  on-disk scan does. The validated package is cached in a `OnceLock` so the
  SHA-256 over 2.7 MB runs once per process, and validation happens on the
  blocking task rather than the command's async caller.
- `build.rs` gains `cargo:rerun-if-changed=resources/project-canvas-template`.
  `include_dir!` expands to one `include_bytes!` per file, so rustc notices
  edits but not a file added or removed; without this line the binary can
  ship a stale file list.
- `scan_package` splits into `ensure_package_dir` + `validate_package_files`.
  The latter is now the single validation gate for both disk-read and
  embedded packages — there is no second, weaker path for the template.
  `prepare_snapshot`, `seed_if_missing`, `get_or_activate`, and `activate`
  take `Option<&ValidatedPackage>`; `accept_agent_update` still passes
  `None` so an agent update can never seed a canvas that does not exist.
- Delete `template_path`/`template_path_from`, the `AppHandle` parameter
  they forced onto two commands, and `bundle.resources` in
  `tauri.conf.json`. With no on-disk copy there is no second source of truth
  to go stale, and the `.app` bundle shrinks by 2.7 MB.
- `ensure_package_dir` replaces `ensure_no_symlink` + `Path::is_dir()` with
  one non-following `symlink_metadata`, so an absent directory is named
  instead of dying unnamed inside the metadata call. Strictly stronger: the
  symlink check still runs on non-following metadata, and the directory test
  now reads off that metadata rather than a link-following `is_dir()`.

Tests: `bundled_template_seeds_a_valid_snapshot` and
`first_activation_seeds_the_validated_template` repoint at the embedded
package and an in-memory map. New `bundled_template_contains_the_expected_tree`
compares the embedded key set against a test-time walk of the source tree,
`bundled_template_paths_are_package_safe` asserts every key is relative and
traversal-free, `missing_package_directory_names_the_path` pins the new
error ordering, and `package_root_symlink_is_rejected` covers the package
root itself, which no existing test did. Each was checked falsifiable:
dropping one embedded file fails the first two, restoring the old ordering
fails the third, and removing the symlink arm fails the fourth.

Verified: Tauri suite passes 3077 tests, clippy -D warnings, rustfmt, and
the file-size gate are clean. `cargo build --bin buzz-desktop` emits no
`resources/` directory next to the executable, so the built binary has no
on-disk template to read. A live canvas open was not exercised — that needs
an interactive `just staging` session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
2026-09-01 16:10:14 +10:00
..