Skip to content

The writ lifecycle surface: reconcile, scopes, and the vocabulary settlement #762

Open
5 of 12 issues completed

Description

@David-Noble-at-work

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/root

The 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

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 failed pkg.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

  1. David-Noble-at-work commented on Sep 27, 2026

    @David-Noble-at-work
    ContributorAuthor

    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 reconcile always 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Area:ArchitectureSystem architecture and designEpic:WritDeploymentwrit deployment: layers, adoption, and driftThread:WritOriginCross-cutting: writ records and verifies where a deployment came fromenhancementNew feature or requestfeatureDeliverable unit within an epic

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions