Repository navigation
The writ lifecycle surface: reconcile, scopes, and the vocabulary settlement #762
Description
The design is settled (2026-08-31). This tracks placing it in the tree: the specification, the plan, the user
documentation, and then the code.
What was ruled
Four lifecycle operations, named once. deploy, reconcile, upgrade, decommission — in both lore
and writ, over different subjects. No program has a status command. writ status is the sole violator
(commands.go:187) and becomes writ reconcile.
The rename is a reversion. writ reconcile existed and was replaced by writ status at phase-8 step 47,
because "'Reconcile' promises mutation the command does not perform" (writ-deploy-family.md:136). That
reason no longer holds: reconcile reports AND repairs. 5.1's "there is no --fix" is the stale part.
Reconcile is git diff for a deployment — what changed since deploy and some number of upgrades — and one
day a merge tool: restore a system from its current state back to its desired state.
Reconcile is a command, not a flag. There is no --reconcile.
Reconcile is valid only after deployment. Before it, not-found — which is what makes the exit code carry
information. This corrects #756, filed against the opposite reading.
The word is scope, not target. A scope is a named execution context: a name, a target root, a
confinement boundary, an elevation posture, and its own graph. 2.4-hermeticity-guarantees.md is the
authority. target keeps its own job: a destination path.
--scope is multi-valued, repeatable, and writ-only. [--scope=<name>]...
Elevation is per scope, automatic, as needed — sudo on Unix, Administrator on Windows. Not soon; the
design is a draft (6.1-privilege-elevation.md).
The builtin scopes
| Scope | Unix | Windows | Elevated |
|---|---|---|---|
Home |
os.UserHomeDir() |
os.UserHomeDir() |
no |
System |
/ |
%SystemDrive%\ |
yes |
ProgramData |
undefined | %ProgramData% |
yes |
ProgramFiles |
undefined | %ProgramFiles% |
yes |
ProgramFilesX86 |
undefined | %ProgramFiles(x86)% |
yes |
Capitalization is the platform's. LOCALAPPDATA and APPDATA are deliberately not builtins: Local has
no supported relocation and reaches through Home as an ordinary relative path; Roaming is officially
redirectable under domain policy, so a builtin would hardcode a location an AD policy may override.
Reserved names
Builtin scope names are reserved on every platform and defined on some:
| Situation | Behaviour |
|---|---|
A layer contains ProgramFiles/ on Unix |
skipped, silently |
--scope=ProgramFiles on Unix |
error: not defined on this platform |
Config declares a custom scope named ProgramFiles, any platform |
error: builtin name |
The general rule: data is skipped, instructions are refused. Reserving everywhere is what keeps one
repository meaning one thing on every machine.
Custom scopes
One config map, name to root, mined from custom segments and converging with them:
writ:
scopes:
Staging: ~/staging/rootThe vocabulary rename
| From | To | Count |
|---|---|---|
TargetName |
ScopeName |
16 |
TargetSpec |
ScopeSpec |
4 |
TargetOrder() |
ScopeOrder() |
3 |
TargetHome() / TargetSystem() |
ScopeHome() / ScopeSystem() |
10 |
--target |
--scope |
1 |
writ.targets |
writ.scopes |
config key, user-visible |
TargetRoot |
unchanged — it is a destination path | 52 |
Target, Entry.Target |
unchanged — the deployed path | 6 |
The evidence is four lines apart in one file: builder.go:49 "Target is the absolute path to the target
file"; builder.go:57 "TargetName is the target scope".
Work
-
10-command-line-interface.md: the four operations,--scope, reserved names, custom scopes -
2.4-hermeticity-guarantees.md: scope vocabulary; remove three stale "unconfined" claims -
5.1-reconciliation.md: reconcile reports AND repairs; the rename and why step 47's reason lapsed - deploy replaces the record: a deploy opens one lifetime and the fold is bounded to it (ruled 2026-09-22, The four lifecycle operations are defined by what each does to the record: deploy replaces it, upgrade updates it, decommission removes it, reconcile restores it #913; the documents carry it, the code does not)
- upgrade updates the record (The four lifecycle operations are defined by what each does to the record: deploy replaces it, upgrade updates it, decommission removes it, reconcile restores it #913)
- decommission removes the record (The four lifecycle operations are defined by what each does to the record: deploy replaces it, upgrade updates it, decommission removes it, reconcile restores it #913)
- reconcile restores the system to the record, the desired state; its words name system-versus-record drift:
absent,changed(The four lifecycle operations are defined by what each does to the record: deploy replaces it, upgrade updates it, decommission removes it, reconcile restores it #913, reconcile's words name system-versus-record drift, the record as the reference: linked, copied, absent, changed; orphan and missing retired #923) -
4.4-root-path-triad.md: §4 Mode Switch describes types that no longer exist -
5.3-recovery-site.md:RootReaderWritertest mode no longer exists -
schema/defaults/writ.yamlandschema/devlore-config.json:writ.scopes, open map, in BOTH - the plan document
-
writ status→writ reconcile, package moved withgit mv, no alias - the
Target*→Scope*rename -
--scope, multi-valued, replacing the inert--target - The doubled environment prefix:
WRIT_WRIT_VERBOSEworks,WRIT_VERBOSEdoes not
Open design questions
- Does a file deployment depend on package installation? (2026-09-05, DANOBLE-WD11-3.) The deploy graph runs the
manifest's package subgraph before the file chains, and one failedpkg.install(818) stopped the whole
scope: no link, no directory, no store was written. A user asking for a working box got nothing because one package
name was wrong. Options: file chains and package subgraphs are independent siblings that fail independently; or the
order stays and a package failure is reported but not fatal to the files; or the dependency is real and stays. A
ruling for this thread.
Related
#761 (System scope roots disagree), #756 (corrected by the not-found ruling), #746 (writ.segments
unwired — the same shape as writ.targets).
Activity
doc(writ): place the lifecycle and scope rulings (#762, #765, #766)
Plan: the writ lifecycle surface — reconcile, scopes, and the vocabul…
Settle writ reconcile's surface, and stop teaching writ status (#773)
writ reconcile: the command is renamed, and its renderings go through…
The four lifecycle operations are defined by what each does to the re…
David-Noble-at-work commentedon Sep 27, 2026
Requirement 2 superseded in part, 2026-09-27
"Reconcile takes no command flags" is broken for one case, knowingly: under the kept-snapshots design (#847),
writ reconcilealways preserves an edit on a retain branch, and restoring the record is a command flag. The owner, choosing that reconcile always retains and the developer chooses to restore: "We break the no-command-flag ruling." Plan: 847-kept-snapshots.md; the plan for this feature carries the note.