Updating a project
Scaffold a product with cargo xtask new and keep it current with tested migrations.
Decisions: ADR-0025, ADR-0026.
Two kinds of change
library code → cargo / pnpm dependency bump
project structure → cargo xtask update
The first is solved by package managers. The second is what makes the difference
between a maintainable base and a template that was copied once — and it is what
update is for.
Running it
cargo xtask update --dry-run # report what would change
cargo xtask update # run the migrations, regenerate, record the version
Each project records the version it tracks:
[origin]
version = "0.2.0"
update runs the migrations between that and the current version, in order, then
regenerates and writes the new version back. Comments and layout in app.toml survive
— it is a file a human wrote and will read again.
A project tracking a newer version than the running build is refused rather than downgraded: upgrade the Origin dependency instead.
What a migration will not do
- It never edits product-owned files (ADR-0022).
- It never guesses where a decision is needed.
Both cases become a checklist:
origin 0.1.0 → 0.2.0
capability files are generated from app.toml
Manual steps required:
→ src-tauri/capabilities/default.json grants permissions no profile covers
(fs:allow-write-text-file, dialog:allow-open). Choose a profile in app.toml, or
declare `hand_written_capabilities = true` under [origin.overrides].
That is the common case for an application that predates Origin. Widening a security profile to fit is exactly the decision a migration must not make on someone’s behalf.
Deliberate deviations
[origin.overrides]
hand_written_capabilities = true
A migration skips what is listed here and reports it as skipped, rather than overwriting a decision someone made on purpose (§46).
How migrations are tested
Against frozen fixture projects in crates/origin-xtask/tests/fixtures/, copied into a
scratch directory and migrated. The tests cover the parts that are easy to get wrong:
- a convertible capability is replaced by a generated one
- one that no profile covers is left untouched, with the question handed back
- running twice changes nothing the second time
--dry-runwrites nothing- comments in
app.tomlsurvive - a project from the future is refused
Without fixtures, migrations are code that runs exactly once — in someone else’s repository.
Starting a new project
cargo xtask new my-app --name "My App" --id dev.example.myapp
The result is not a blank window: it is the architecture contract, logging, error
handling, settings, secrets, storage, a security profile, CI, and a module that is
already testable without a desktop session. Its xtask is three lines, so the rules
and the generator arrive with a version bump.
Scaffolding uses released registry versions by default. Pass --local to point the
generated project at the current Origin checkout instead. Origin’s own CI always tests
that local form against main (ADR-0026).