Skip to content

writ upgrade neither pins layers nor records origin: a regenerated template or secret has no provenance #844

Open

Description

@David-Noble-at-work

writ deploy pins every layer to a detached worktree at HEAD before planning and records the
commit hashes and dirty-layer list in its graph's origin annotations (PinInfo, plan.go;
pkg/op/graph.go:702). writ upgrade does neither.

Observed, 2026-09-06

upgrade.Execute (cmd/writ/writ/upgrade/upgrade.go) folds the copied inventory from the store,
classifies each entry, and re-runs the planned chains against the layer sources as they are in the
working tree
— no snapshot.Pin, no PinInfo. Its graph's origin is
NewOriginBase("writ", scope, {run_root, …}): tool, scope, run root. No commit_hashes, no
dirty_layers. Its trace, like deploy's, carries no origin at all.

The consequence is sharper than deploy's gap. A deployed link serves the working tree by
construction and the graph at least names the commit that was planned. A regenerated copy — an
expanded template, a decrypted secret — is a snapshot of content taken at a moment, and nothing
records which commit of which layer it came from, or whether that layer was dirty. It is the one
place in writ where the answer to "where did this file come from" is genuinely lost.

Wanted

  1. Upgrade pins the way deploy does — snapshot.Pin per layer, refuse dirty without
    --allow-dirty, plan against the snapshot — so a regenerated copy is reproducible from a commit.
  2. Its graph carries the same origin annotations — commit_hashes, dirty_layers — so the two
    operations' graphs read alike.
  3. Then Record the deployed commit for each layer source #473 surfaces both in the trace and in reconcile, and a copied file's provenance is a
    question a user can ask.

One rule, stated once: every operation that reads a layer pins it and records the pin. Deploy
does; upgrade should; adopt and migrate are worth checking against the same sentence.


Epic: #466 · Feature: #472

Activity

  1. added
    taskImplementation work item
    Epic:LayerSplitSplit the dotfiles into base, team, and personal layers
    Thread:WritOriginCross-cutting: writ records and verifies where a deployment came from
    on Sep 6, 2026
  2. David-Noble-at-work commented on Sep 28, 2026

    @David-Noble-at-work
    ContributorAuthor

    Closes with #928 (lane 11 of #916), 2026-09-28: under kept snapshots (#847, ruling 5) upgrade pins its own ref and moves links with copies.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Epic:LayerSplitSplit the dotfiles into base, team, and personal layersThread:WritOriginCross-cutting: writ records and verifies where a deployment came fromtaskImplementation work item

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions