Deterministic generation
Understand Astilba Create's planning, validation, staging, publication, and failure boundaries.
Create plans and validates the complete project before it publishes a destination. It rejects ambiguous merges and portable-path hazards instead of guessing how to resolve them.
Follow the generation sequence
Section titled “Follow the generation sequence”For one generator version, recipe version, and validated metadata, Create follows this sequence:
- resolve exactly one named recipe and its required internal profiles;
- validate project metadata and every output declaration;
- reject file, directory, case-insensitive, and symbolic-link collisions;
- construct the deterministic manifest and sorted project plan;
- write files and links into a temporary sibling directory;
- apply file and directory modes;
- optionally initialize an isolated Git repository;
- publish the complete top-level tree into a new destination; and
- optionally install dependencies in the published project.
--dry-run stops after planning and returns the file and link paths without writing anything. It validates the plan, not the target filesystem or external operations.
Know what deterministic means
Section titled “Know what deterministic means”The same Create version, recipe, and metadata produce the same planned regular-file bytes, path order, file modes, and symbolic-link targets. The manifest digests are therefore stable for the same plan.
Fresh .git internals and an installed node_modules tree are outside that byte-for-byte contract. Git still receives a stable structural guarantee: Create starts a new repository on main, without template history or an initial commit.
Recipe identifiers and recipe versions are permanent contract coordinates. Updating a recipe requires a new version; it does not silently redefine the recorded v2 output.
Reject unsafe destinations
Section titled “Reject unsafe destinations”The command-line destination must be a normalized portable relative path. Create rejects:
- absolute POSIX or Windows paths;
.or..path segments and traversal;- backslashes;
.gitsegments;- Windows device names such as
CONorNUL; - trailing spaces or periods;
- control or formatting characters;
- non-ASCII path segments; and
- characters that are not portable to Windows.
During actual creation, the resolved destination must not already exist. Create makes missing parent directories one segment at a time and rejects symbolic links or non-directory entries in the existing ancestry. Dry runs skip this filesystem preparation and its checks.
These rules are stricter than the host filesystem because a generated repository should remain portable when checked out elsewhere.
Reject ambiguous output plans
Section titled “Reject ambiguous output plans”Before writing, Create rejects:
- two declarations for the same path;
- paths that differ only by letter case;
- a regular file used as another output’s parent directory;
- a profile dependency cycle or conflict;
- a symbolic link that collides with another output;
- a symbolic link whose target is not a planned regular file; and
- any recipe output beneath the reserved
.astilbamanifest directory.
Create does not have a generic “merge on collision” rule. Each named recipe must resolve to one internally consistent project.
Publish only a complete tree
Section titled “Publish only a complete tree”Create stages output in a temporary directory beside the destination. File writing, link creation, permission changes, and optional Git initialization happen there.
Publication creates the destination with an incomplete marker, moves the staged top-level entries, then removes the marker only after every move succeeds. If a move fails, Create attempts to roll all moved entries back:
- when rollback succeeds, it removes the marker and tries to remove the now-empty destination;
- when any rollback also fails, it preserves the incomplete marker so the directory cannot be mistaken for a successful project.
Create never recursively deletes a destination that another process may have changed. Cleanup failures cannot replace the publication result: an incomplete destination remains reported as incomplete, while a failure to remove an already empty staging directory does not turn a complete project into a failed generation.
Human recovery output names the relevant outcome. If the incomplete marker remains, inspect the destination and either recover or remove it before running Create again.
On Windows, CLAUDE.md requires symbolic-link permission. Enable Developer Mode or use an elevated shell. If the link cannot be created, staging fails before publication.
Isolate Git initialization
Section titled “Isolate Git initialization”Git initialization removes ambient GIT_* variables and disables global and system configuration. It also supplies an empty temporary template directory.
This prevents a developer’s hooks, templates, aliases, or global defaults from modifying the generated repository. Create verifies that .git is a real directory and that HEAD points to refs/heads/main.
Treat installation as a separate phase
Section titled “Treat installation as a separate phase”Dependency installation is deliberately outside atomic generation:
- Create uses an installed
pnpmonly when its version exactly matches the generated project’s pinned version. - Otherwise, it asks Corepack for that exact pnpm version before installing.
- A package-manager failure leaves the generated project intact and reports that installation must be retried.
This boundary avoids deleting a valid project because a registry, network, lifecycle script, or local package-manager configuration failed.
Do not use Create as an updater
Section titled “Do not use Create as an updater”Create 0.3.0 only creates a destination that does not exist. It does not regenerate over an existing repository, merge a newer recipe, run doctor, or update a default branch.
The project manifest records enough ownership evidence for future explicit migrations, but no migration command is shipped in this release.