Expand description
Safe Executor — 0.5.2.
The layer that turns a reviewed PlanDocument into deterministic
on-disk changes. It behaves like a cautious senior engineer applying
changes: if anything is uncertain, it refuses.
§Posture
- Refusal-first. Every primitive outside a tight safe subset is
rejected with a named
ExecutionErrorvariant. The supported set for 0.5.2 is small by design (add_field,rename_field); the list grows only as each primitive’s edit + migration story is proven safe. - All-or-nothing. A partial apply is worse than no apply. The
executor builds the full change set first (dry-run), verifies every
precondition, then commits atomically — writing every target to a
sibling
.tmpand renaming on success. A mid-flight failure rolls back everything touched. - Never writes arbitrary SQL. The only SQL that can land on disk
comes from
build_migration_sql, whose shape is entirely determined by the primitive being applied. There is no path from an AI prompt to a hand-written SQL statement. - Every check runs twice. The executor re-runs
Plan::validateandreview_planbefore touching anything, even though the document was validated when saved. Schemas drift, and this layer is the last place to catch that drift before a migration is written.
§What 0.5.2 supports
Primitive::AddField— adds a column viaALTER TABLE … ADD COLUMN …and patches the generatedapps/<app>/models.rs(struct,COLUMNS,INSERT_COLUMNS,from_row,insert_values). Addsuse chrono::{DateTime, Utc};if the new field needs it and the file doesn’t already import it.Primitive::RenameField—ALTER TABLE … RENAME COLUMNplus a scoped rename inside the same models.rs.
§What 0.5.2 refuses
Every other primitive returns ExecutionError::UnsupportedPrimitive
with a one-line reason. These land in later pull requests, not silent
“best effort” writes:
add_model,remove_model,rename_model— require cross-file scaffolding (apps tree, migrations, admin + views updates).remove_field,remove_relation— destructive; gated on a--forcestyle flag that doesn’t ship in 0.5.2.change_field_type,change_field_nullability— require SQLite table-recreation migrations which need their own review pass.add_relation,update_admin— out of scope for 0.5.2.create_migration— developer-only; refused at theExecutionError::DeveloperOnlyForbiddengate before it ever reaches the dispatch.
§Testability
The core logic (plan_execution) is pure: it takes a
ProjectView (in-memory snapshot of the files it cares about) and
returns an ExecutionPreview. No filesystem I/O. The impure entry
execute_plan_document wraps it with disk reads, the
confirmation-friendly preview, and atomic writes.
Structs§
- Execute
Options - Knobs for
execute_plan_document. Kept as a struct so the executor can grow flags without a breaking signature change. - Execution
Preview - Dry-run output. Produced by
plan_executionbefore any write, and displayed by the CLI as the “Plan to apply” preview. - Execution
Result - Reported after a successful apply. Filenames are relative to the
project root passed to
execute_plan_document. - Parsed
Models File - Planned
File Change - One file the executor will write.
Createexpects the file to not exist;Updateexpects it to matchexpected_current_contentsbyte for byte — any mismatch means a human touched the file after the plan was reviewed, which is aExecutionError::FileConflict. - Project
View - Parsed view of the project files the executor cares about.
- Retrofit
Report - Result of scanning a schema for belongs_to relations that were
materialised before 0.9.0 (no
on_delete/requiredmetadata).
Enums§
- Execution
Error - Every way the executor can refuse. All variants are refusal-first: nothing has been written when one of these is returned.
- File
Change Kind
Functions§
- execute_
plan_ document - Run a plan against the project on disk.
- plan_
execution - Compute the exact set of file changes that executing
docwould produce, without touching the filesystem. - plan_
retrofit_ foreign_ keys - Phase 2: generate a retrofit plan for every belongs_to relation
that lacks
on_deletemetadata. Returns a report the caller can either print (dry-run) or materialise (write migrations + update schema). - render_
preview_ human - Render an
ExecutionPreviewas an operator-friendly block. The CLI prints this before asking for confirmation.