Skip to content

writ adopt carries the platform (--platform <token>) and leaves a deployment record: its graph carries the files annotation and its trace appends to the current lifetime #931

Closed

Description

@David-Noble-at-work

Epic: #451 · Feature: #762 The writ lifecycle surface · Schedule: #916, lane 14, PR A · Waits on: #922 (lane 1)

The ruling, 2026-09-23

An adopted file is deployed: after writ adopt, the record says so, reconcile says linked, and the next
deploy recognizes the link as writ's own. Adopt copies content into a project location within a scope, in one of
the three layers, and it must carry platform information: the caller names the platform label, and the file
lands in the suffixed project directory the deploy walk reads.

Today

writ adopt [--layer personal|team|base] --project <name> <item>... moves each item to
<layer>/<scope>/<project>/<rel> and links it back, inferring the scope (Home or System) from the item's location,
and runs each scope's adoptions as one execution graph whose trace persists. Two gaps:

  1. No platform. Every adoption lands in the platform-neutral project directory. common.Darwin,
    noblefactor-ops.Linux.Debian, personal.Windows cannot be targeted.
  2. The trace is not a deployment record. Adopt's graph origin carries no files annotation (target, source,
    action, layer, project, scope), which is what readback.Fold reads to know what a run left. So an adopted link is
    invisible to reconcile, and the next deploy under stop refuses it as foreign.

The change

  1. --platform <token>, optional. The vocabulary is platform.Token's -- Darwin, Windows, Linux.Debian,
    Linux.Fedora, ... -- plus Unix; anything else is a usage error (64), so a typo cannot mint a suffix the walk
    never reads. The destination becomes <layer>/<scope>/<project>.<token>/<rel>; absent, the neutral directory as
    today.
  2. The graph carries the files annotation deploy's carries, one entry per adopted file: target (the original
    location, now the link), source (the project location), action file.link, layer, project, scope, and
    target_root. The graph is built and run as today; the trace it leaves is then a deployment record the fold
    reads exactly as it reads a deployed link.
  3. The trace appends to the current lifetime (deploy replaces the record: readback.Fold is bounded to the current lifetime, one writ deploy invocation across scopes #922): adopt is a write into the current deployment. With no
    current lifetime, adopt refuses with not-found (66): there is no deployment to adopt into.
  4. Scope inference uses the scope set of the platform (--scope, multi-valued and writ-only, replaces the inert --target: absent means every scope defined on this platform; Home and System are well known, Windows adds ProgramFiles and ProgramData, and writ.scopes adds the rest #926 once it lands): a Windows item under %ProgramData%
    infers ProgramData.

Acceptance

  • --platform <suffix> lands the file under <layer>/Home/<project>.<suffix>/, the suffix in the segment vocabulary (ruled 2026-09-23); --platform Ubuntu is a usage error (64) naming the vocabulary: TestAdopt_Platform, both VMs
  • after an adopt, reconcile reports the link linked and a deploy under stop accepts it: TestAdopt_LeavesADeploymentRecord, both VMs (2026-09-23)
  • the adopt's trace is in the current lifetime's runs (both VMs: the lifetime's last run is adopt); with no current lifetime, adopt exits 66 (RequireCurrentLifetime, deploy replaces the record: readback.Fold is bounded to the current lifetime, one writ deploy invocation across scopes #922)
  • the writ adopt Long and example document --platform and the vocabulary
  • both VMs: adopt one file with the platform's own suffix, reconcile linked, deploy accepted, undone (2026-09-23)

Plan and design documents

Kind Document
Plan a lane plan arrives with the branch (PR A, beside #922)
Design 10-command-line-interface.md, 5.1-reconciliation.md, docs/plans/extract-starlark-from-op/phase-8/writ-adopt-command.md

Landed 2026-09-23 (lane 14, PR A)

One chain per item and the segment vocabulary, ruled 2026-09-23. Two findings on the way: a graph holding a
flow.choose does not load back (#939), so the existing-destination guard runs before the graph; and adopt had never
worked on a real install, because a registered layer is a symlink and the confined run root refused to write through
it -- the layer path now resolves through its symlink.

Metadata

Metadata

Labels

Epic:WritDeploymentwrit deployment: layers, adoption, and drifttaskImplementation work item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions