pub fn ensure_devflow_dir(dir: &Path) -> Result<()>Expand description
Create dir (and any missing parents), then self-protect any .devflow
directory found in its path by writing <that-dir>/.gitignore containing
* — so a downstream user’s routine git add . && git commit never
sweeps DevFlow’s runtime artifacts (agent stdout, gate context, state)
into their repository, independent of whether their own root
.gitignore mentions .devflow at all (closes 19a-WR-01,
19-CONTEXT.md D-14).
This is deliberately a different function from the pure path accessor
devflow_dir above: devflow_dir(project_root) takes a project
root and returns project_root/.devflow with zero filesystem I/O — it
is invoked from read-only paths (doctor, status) and from tests that
assert on the returned path, so giving it side effects would be exactly
the class of behavioral change this phase exists to avoid. This function,
ensure_devflow_dir(dir), instead takes the directory to create,
which may itself be .devflow, a subdirectory of it, or something with no
.devflow ancestor at all. Do not confuse the two.
Contract:
create_dir_all(dir)— createdirand all missing parents.- Find the shallowest
.devflowpath component (see [find_devflow_marker]). - If found, write
<marker>/.gitignorewith the bytes*\n, usingcreate_new(true)so an existing file — whatever its content — is left untouched; a lost race against a concurrent creator surfaces asAlreadyExists, which this function maps toOk(()). Any other I/O error propagates via?. - If no
.devflowcomponent exists, this function is exactly equivalent tocreate_dir_all.
Returns std::io::Result<()>, not a crate-specific error enum: this
plan’s seven conversion sites each live in a different module with their
own error enum (WorkflowError, GateError, MonitorError,
ResultError, ShipError, LockError), and every one already carries an
Io(#[from] std::io::Error) variant, so ? converts at every call site
with zero signature churn.
Deleted-marker note: if .devflow/.gitignore is deleted after
creation, subsequent calls will not recreate it — the protection is
established once per directory lifetime. Recreating a deleted marker
would violate the rule that this function must never overwrite an
existing .gitignore a user or another tool may own, and it cannot
distinguish “user deleted it” from “never created.”