file

package
v0.1.0-dev.20260818235708 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package file provides file system actions for the operation graph.

Index

Constants

View Source
const (
	Backup     op.ActionName = "file.backup"
	Copy       op.ActionName = "file.copy"
	Exists     op.ActionName = "file.exists"
	Find       op.ActionName = "file.find"
	Glob       op.ActionName = "file.glob"
	IsDir      op.ActionName = "file.is_dir"
	IsFile     op.ActionName = "file.is_file"
	Join       op.ActionName = "file.join"
	Link       op.ActionName = "file.link"
	Mkdir      op.ActionName = "file.mkdir"
	Move       op.ActionName = "file.move"
	Name       op.ActionName = "file.name"
	Observe    op.ActionName = "file.observe"
	Parent     op.ActionName = "file.parent"
	ReadBytes  op.ActionName = "file.read_bytes"
	ReadText   op.ActionName = "file.read_text"
	Remove     op.ActionName = "file.remove"
	RemoveAll  op.ActionName = "file.remove_all"
	Root       op.ActionName = "file.root"
	Unlink     op.ActionName = "file.unlink"
	WalkTree   op.ActionName = "file.walk_tree"
	WriteBytes op.ActionName = "file.write_bytes"
	WriteFile  op.ActionName = "file.write_file"
	WriteText  op.ActionName = "file.write_text"
)

Action-name constants for the file provider's plan-mode actions.

Each constant is the short dotted action label its method dispatches under. Pass these to plan.Plan, op.ReceiverRegistry().BuildAction, RuntimeEnvironment.ActionByName, or WithActionNamed in place of a string literal so a typo is a compile error and rename / find-references work through the constant.

Variables

View Source
var (
	// SkipDir indicates that the current directory should be skipped.
	SkipDir = fs.SkipDir

	// SkipAll signals the walker to terminate immediately (success).
	SkipAll = fs.SkipAll
)

Functions

This section is empty.

Types

type Directory

type Directory struct {
	Resource
}

Directory is the taxonomy variant asserting that its path names a directory (phase-8 step 23).

The kind is declared intent, never stat-assigned (ruling 1): planning is offline, so the assertion is verified at use rather than at construction — Directory.Digest and Directory.Etag observe the disk with lstat semantics and error with a kind mismatch when the entry is anything else (ruling 5e). Identity is the embedded Resource (URI + SourcePath); runtime-observed metadata lives on *Observation, exactly as for the base.

func DiscoverDirectory

func DiscoverDirectory(runtimeEnvironment *op.RuntimeEnvironment, value any) (*Directory, error)

DiscoverDirectory registers a file.Directory via op.ResourceCatalog.Discover without claiming production.

The discovery counterpart of NewDirectory: no producer is stamped, so no unit reference is taken. Nil-Catalog tolerance returns the unlinked candidate.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `value`: a string file path or file URI.

Returns:

  • `*Directory`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, the input violates RFC 8089 when in file URI form, the catalog's strict assertions fail, or the URI's existing entry is another kind.

func NewDirectory

func NewDirectory(runtimeEnvironment *op.RuntimeEnvironment, producerID string, value any) (*Directory, error)

NewDirectory constructs a file.Directory and claims production via op.ResourceCatalog.GetOrCreate.

Use NewDirectory from a producer dispatch context; the returned Directory is the canonical catalog entry, stamped with the given `producerID` when non-empty. A catalog entry already claimed under a different kind for the same URI is an error — cross-kind plan conflicts surface at the earliest moment. Nil-Catalog tolerance: the candidate is returned unlinked when no catalog is present.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `producerID`: the producing caller's id (`activationRecord.CallerID` — a unit id under graph dispatch, a starlark call-site under script dispatch), or "" for caller-less dispatch (an empty producer stamp).
  • `value`: a string file path or file URI.

Returns:

  • `*Directory`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, the input violates RFC 8089 when in file URI form, the catalog's strict assertions fail, or the URI's existing entry is another kind.

func (*Directory) CanConvertFrom

func (*Directory) CanConvertFrom(source reflect.Type) bool

CanConvertFrom reports whether `source` can be projected into a *Directory via Directory.ConvertFrom.

The variant's own probe for the framework's op.TargetConverter contract — defined directly (not promoted from the embedded base) because the cheap-probe contract calls it against a nil-or-zero `*Directory` receiver, and a promoted method would dereference the nil receiver to reach the embedded base. Today's accepted source shape is `string`, interpreted as a filesystem path under the active fsroot.

Parameters:

  • `source`: the candidate source type to test.

Returns:

  • `bool`: true when `source` is `string`.

func (*Directory) ConvertFrom

func (*Directory) ConvertFrom(value any) (any, error)

ConvertFrom projects `value` into a fresh *Directory.

Mirrors Resource.ConvertFrom: the returned value carries the path under SourcePath but is NOT catalog-interned at this layer; receiving provider methods intern via their own NewDirectory/DiscoverDirectory path.

Parameters:

  • `value`: the source value; must be `string`.

Returns:

  • `any`: the constructed unlinked *Directory.
  • `error`: non-nil when `value` is not a `string`.

func (*Directory) Digest

func (r *Directory) Digest() (op.Digest, error)

Digest returns the Merkle root of the directory tree (phase-8 step 23 — the chartered scheme).

Always fresh: the disk is observed at call time. Each directory's digest is a sha256 over its immediate entries in byte-wise lexicographic name order (the fs.ReadDir guarantee — platform-stable, ruling 5c), each entry contributing an unambiguous record: one kind marker byte ('f' regular file, 'd' directory, 'l' symlink), the entry name, a NUL delimiter, and the entry's 32-byte digest. A regular file digests by content (streamed sha256); a symlink digests by the sha256 of its literal readlink target, never following (matching ruling 5a); a subdirectory digests by its own Merkle root, recursively. Entry names carry no path separators, so the serialization is identical on every platform, and only the tree's own shape and content participate — the enclosing absolute path does not.

The root covers everything (ruling 5d): no gitignore filtering and no `.git` skip — a digest that skips content would report "unmodified" over a modified tree. The empty directory digests deterministically (the hash over zero entries). An entry of any other kind (FIFO, socket, device) is an error: a digest cannot honestly identify what it cannot hash. The entry itself must be a directory — the kind check uses lstat semantics, and any other observed kind errors with a kind mismatch (ruling 5e).

Returns:

  • `op.Digest`: sha256 algorithm with 32 raw bytes — the Merkle root.
  • `error`: an lstat error, a kind mismatch, an unsupported entry kind, or any read error during the walk.

func (*Directory) Equal

func (r *Directory) Equal(other any) bool

Equal reports whether `r` and `other` identify the same directory resource.

Strict equality mirroring Resource.Equal: `other` must be a *file.Directory — the same URI held by another kind (or by the catch-all base) does not match. Once the type check passes, URI comparison is delegated to op.ResourceBase.Equal.

Parameters:

  • `other`: the value to compare against; may be `any`, including nil or a non-Directory.

Returns:

  • `bool`: true if `other` is a *file.Directory with the same URI as `r`.

func (*Directory) Etag

func (r *Directory) Etag() (string, error)

Etag returns the inexpensive stat-derived change-detection token for the directory.

The cheap counterpart of the Merkle-root Directory.Digest (the chartered pairing): the disk is observed at call time with lstat semantics, a kind other than directory errors with a kind mismatch (step 23, ruling 5e), and the token is the shared stat-tuple form: a sha256 of (size, mtime_ns, ino) packed little-endian, encoded as lowercase hex. A directory's mtime moves on immediate-child creation, deletion, and rename, so the Etag is a shallow signal: the catalog treats a changed Etag as the trigger for the full Digest comparison, exactly as for regular files.

Returns:

  • `string`: lowercase hex sha256 of the packed stat tuple.
  • `error`: an lstat error or a kind mismatch.

func (*Directory) String

func (r *Directory) String() string

String returns a debug-oriented single-line representation of the directory resource.

Returns:

  • `string`: `file.Directory{uri=<URI>, source_path=<path>}`.

func (*Directory) UnmarshalJSON

func (r *Directory) UnmarshalJSON(data []byte) error

UnmarshalJSON populates the receiver from a JSON-encoded string (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant — defined directly so rehydration rebuilds a *Directory, never a half-filled embedded base.

Parameters:

  • `data`: JSON-encoded string containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the JSON does not decode as a string, or resource construction fails.

func (*Directory) UnmarshalText

func (r *Directory) UnmarshalText(text []byte) error

UnmarshalText populates the receiver from raw UTF-8 bytes containing a file path or file URI.

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant.

Parameters:

  • `text`: UTF-8 bytes containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing or resource construction fails.

func (*Directory) UnmarshalYAML

func (r *Directory) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML populates the receiver from a YAML scalar (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant.

Parameters:

  • `unmarshal`: callback supplied by the YAML decoder that projects the current node into the given target.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the YAML node does not decode as a string, or resource construction fails.

type Entry

type Entry interface {
	op.Resource

	// Path returns the canonicalized absolute path handle on the disk.
	Path() fsroot.Path
	// contains filtered or unexported methods
}

Entry is the mixed-kind currency for "any file resource" — the interface the taxonomy's variants implement.

Modeled after the standard library's fs.DirEntry precedent (phase-8 step 23, ruling 4): contexts that legitimately traffic in mixed or observed kinds — enumeration returns, per-entry walker callbacks, observation minting — accept or return an Entry rather than a concrete variant. Contexts whose semantics fix the kind use the concrete variant (*Regular, *Directory, *SymbolicLink) directly, and a plain string path is the currency for create/update/delete parameters (ruling 2 — the resource is the product of a mutation, never its input).

type MutationKind

type MutationKind string

MutationKind identifies the filesystem mutation a Receipt records, so Provider.CompensateFileMutation can invert it: remove a created file or directory, restore prior content from recovery for an update or delete, or recreate a removed directory.

const (
	// MutationCreateFile records a file that did not exist before the write action; its undo removes the file.
	MutationCreateFile MutationKind = "create_file"

	// MutationUpdateFile records a file whose prior content was archived to recovery before an overwrite action; its
	// undo restores that content.
	MutationUpdateFile MutationKind = "update_file"

	// MutationDeleteFile records a file removed after its content was archived to recovery; its undo restores it.
	MutationDeleteFile MutationKind = "delete_file"

	// MutationCreateDir records a directory the call created; its undo removes it.
	MutationCreateDir MutationKind = "create_dir"

	// MutationDeleteDir records a directory the call removed; its undo recreates it.
	MutationDeleteDir MutationKind = "delete_dir"
)

type Observation

type Observation struct {
	op.ObservationBase

	// Size is the file size in bytes at observation time. Zero when `Exists` is false.
	Size int64

	// Mode is the file mode bits at observation time. Zero when `Exists` is false.
	Mode os.FileMode

	// ModTime is the file modification time at observation time. Zero value when `Exists` is false.
	ModTime time.Time

	// Inode is the filesystem inode number at observation time. Zero when `Exists` is false or on
	// platforms that do not expose inode information.
	Inode uint64

	// Device is the filesystem device id at observation time. Zero when `Exists` is false or on
	// platforms that do not expose device information.
	Device uint64
}

Observation captures the runtime-observed state of a *Resource at the moment it was observed.

Distinct from Resource, which carries identity only. An observation is a point-in-time metadata snapshot record — not a Resource, never cataloged — whose identity comes from the resource it references (op.ObservationBase.OfResource, by pointer value). It embeds op.ObservationBase (the back-link + op.ObservationBase.Exists) and adds the file-specific measurement fields: `Size`, `Mode`, `ModTime`, `Inode`, `Device`.

func NewObservation

func NewObservation(
	ofResource Entry,
	exists bool,
	size int64,
	mode os.FileMode,
	modTime time.Time,
	inode uint64,
	device uint64,
) *Observation

NewObservation constructs a *Observation anchored to the resource it observes.

Parameters:

  • `ofResource`: the Entry this observation is of. Must be non-nil (asserted by op.NewObservationBase).
  • `exists`: true when the file existed at observation time.
  • `size`: file size at observation time.
  • `mode`: file mode bits at observation time.
  • `modTime`: file modification time at observation time.
  • `inode`: filesystem inode at observation time.
  • `device`: filesystem device id at observation time.

Returns:

  • `*Observation`: the constructed observation.

func (*Observation) String

func (o *Observation) String() string

String returns a debug-oriented single-line representation of the observation.

Returns:

  • `string`: `file.Observation{of=<OfResource.URI()>, exists=<bool>, size=<bytes>, mode=<mode>}`.

type Provider

type Provider struct {
	op.ProviderBase
}

Provider provides file system actions.

+devlore:access=both

func NewProvider

func NewProvider(runtimeEnvironment *op.RuntimeEnvironment) *Provider

NewProvider creates a file provider bound to the given context.

func (*Provider) Backup

func (p *Provider) Backup(
	activationRecord *op.ActivationRecord,
	sourcePath string,
	backupSuffix string,
) (Entry, *Receipt, error)

Backup moves the entry at `sourcePath` to a timestamped backup location, delegating to Provider.Move.

Takes a path, not a resource (step 23, ruling 2): Backup renames — it never reads content — so the entry it displaces is identified by location and the produced backup resource is the return value.

Parameters:

  • `activationRecord`: the dispatch activation threaded to Provider.Move.
  • `sourcePath`: the path of the entry to back up.
  • `backupSuffix`: the suffix inserted before the timestamp; empty defaults to the runtime environment's `BackupSuffix` (the spec path derives it as ".<ProgramName>-backup", e.g. ".devlore-backup").

Returns:

  • `Entry`: the backup destination resource, minted as the moved entry's observed kind.
  • `*Receipt`: the compensation receipt for undo.
  • `error`: non-nil on move failure.

func (*Provider) CompensateFileMutation

func (p *Provider) CompensateFileMutation(activationRecord *op.ActivationRecord, receipt *Receipt) error

CompensateFileMutation inverts any file or directory mutation by dispatching on the receipt's MutationKind.

It is the single undo for every file.Receipt: a receipt names [compensateFileMutationAction] as its compensating action at construction, so the recovery machinery routes here regardless of which method or dispatcher produced it. Create / update / delete of a file restores via [Provider.compensateWrite] (remove the new file, restore any archived predecessor, prune boundary directories) — except a file receipt that recorded a source (a move), which reverses via [Provider.compensateMove]. A directory create reverses via [Provider.compensateMakeDir] and a directory delete via [Provider.compensateRemoveDir].

Parameters:

  • `activationRecord`: the dispatch activation (the required floor for compensating actions — step 27).
  • `receipt`: the *Receipt to invert; a nil receipt is a no-op.

Returns:

  • `error`: the underlying compensation error, or a wrapped error for an unknown kind.

func (*Provider) CompensateWalkTree

func (p *Provider) CompensateWalkTree(activation *op.ActivationRecord, stack *op.RecoveryStack) error

CompensateWalkTree unwinds the op.RecoveryStack returned by Provider.WalkTree in LIFO order.

Parameters:

Returns:

  • `error`: non-nil when unwinding any recorded compensation fails.

func (*Provider) Copy

func (p *Provider) Copy(
	activationRecord *op.ActivationRecord,
	source *Regular,
	destinationPath string,
	mode os.FileMode,
	user string,
	group string,
) (product *Regular, receipt *Receipt, err error)

Copy copies `source`'s contents to a new file at `destinationPath` with the given mode and ownership.

`user` and `group` each accept a name or a decimal id, and either may be empty to leave that side unchanged. When either is set they are resolved and applied via os.Chown after the file is created.

Parameters:

  • `activationRecord`: the dispatch activation; its `Unit` stamps the produced *Regular's producerID.
  • `source`: the *Regular whose contents are copied — a content read, so the parameter is the resource (step 23, ruling 2).
  • `destinationPath`: the destination path for the new file.
  • `mode`: the os.FileMode applied to the created file.
  • `user`: the owner, by name or decimal uid; empty leaves the owner unchanged.
  • `group`: the group, by name or decimal gid; empty leaves the group unchanged.

Returns:

  • `*Regular`: the created destination resource, resolved against the filesystem.
  • `*Receipt`: the compensation receipt for undo.
  • `error`: non-nil on resource construction, write preparation, copy, ownership, or resolve failure.

+devlore:defaults mode={{ umask 0o755 }}, user="", group=""

func (*Provider) Exists

func (p *Provider) Exists(path string) (bool, error)

Exists reports whether an entry exists at `path`, examining the link itself (lstat semantics).

A location query takes a path (step 23, ruling 2) — no content is read and no resource is minted. A not-exist result is reported as `(false, nil)`, not an error; only a genuine stat failure returns a non-nil error.

Parameters:

  • `path`: the path to probe.

Returns:

  • `bool`: true when an entry exists at the path.
  • `error`: non-nil on any stat failure other than not-exist.

func (*Provider) Find

func (p *Provider) Find(pattern string, includeGitignored bool) (product []Entry, err error)

Find returns the file resources matching `pattern`, with recursive `**` support, beneath the scoped root.

The pattern is split into a base directory and a match expression; the base is resolved against the scoped root and must not escape it. Matching walks the tree, skipping gitignored entries unless `includeGitignored` is set.

Parameters:

  • `pattern`: the glob pattern, which may contain `**` for recursive matching.
  • `includeGitignored`: when false, entries matched by gitignore rules are skipped.

Returns:

  • `[]Entry`: the matching entries, in walk order, each minted as its observed kind.
  • `error`: non-nil when the pattern escapes the scoped root, or on tracker construction or walk failure.

+devlore:defaults includeGitignored=false

func (*Provider) Glob

func (p *Provider) Glob(pattern string, includeGitignored bool) ([]Entry, error)

Glob returns the Resource entries for filesystem paths matching `pattern` via filepath.Glob.

Unlike Provider.Find, matching is non-recursive (no `**`). Gitignored matches are dropped unless `includeGitignored` is set; a gitignore tracker that fails to construct degrades to returning all matches.

Parameters:

  • `pattern`: the filepath.Glob pattern to match.
  • `includeGitignored`: when false, matches filtered by gitignore rules are dropped.

Returns:

  • `[]Entry`: the matching entries, each minted as its observed kind.
  • `error`: non-nil on a malformed pattern.

+devlore:defaults includeGitignored=false

func (*Provider) IsDir

func (p *Provider) IsDir(path string) (bool, error)

IsDir reports whether `path` exists and is a directory, following symlinks (stat semantics).

A location query takes a path (step 23, ruling 2). A not-exist result is reported as `(false, nil)`, not an error.

Parameters:

  • `path`: the path to probe.

Returns:

  • `bool`: true when the path exists and is a directory.
  • `error`: non-nil on any stat failure other than not-exist.

func (*Provider) IsFile

func (p *Provider) IsFile(path string) (bool, error)

IsFile reports whether `path` exists and is a regular file, following symlinks (stat semantics).

A location query takes a path (step 23, ruling 2). A not-exist result is reported as `(false, nil)`, not an error.

Parameters:

  • `path`: the path to probe.

Returns:

  • `bool`: true when the path exists and is a regular file.
  • `error`: non-nil on any stat failure other than not-exist.

func (*Provider) Join

func (p *Provider) Join(parts ...string) string

Join joins path components using the OS path separator via filepath.Join.

Parameters:

  • `parts`: the path components to join.

Returns:

  • `string`: the joined path, OS-native.

Native, unlike Provider.Name and Provider.Parent: those answer questions ABOUT a path as a value, while Join builds one FOR USE — its result is handed to the filesystem.

func (p *Provider) Link(
	activationRecord *op.ActivationRecord,
	sourcePath string,
	targetPath string,
	verbatim bool,
) (product *SymbolicLink, receipt *Receipt, err error)

Link creates a symbolic link at `targetPath` pointing to `sourcePath`, archiving any existing entry first.

Takes paths, not resources (step 23, ruling 2): the symlink stores a name — nothing is read from the source, which may legally dangle. By default the stored name is `sourcePath` canonicalized to its absolute form (the deploy posture: links across trees stay valid from any working directory); with `verbatim` set, the LITERAL `sourcePath` string becomes the link's content, uninterpreted (the extraction posture — archive §10 ruling 1a: a tar entry's relative target lands on disk exactly as archived, which also keeps the SymbolicLink.Digest literal-target hash faithful to the archive). When an entry already exists at `targetPath`: if it is a symlink already pointing at the stored name, Link is a no-op; otherwise the existing entry is archived to the op.RecoverySite before the new link is created. When nothing exists, the parent directory chain is created and its boundary recorded on the receipt for compensation.

Parameters:

  • `activationRecord`: the dispatch activation; its `Unit` stamps the produced *SymbolicLink's producerID.
  • `sourcePath`: the path the link points to.
  • `targetPath`: the path at which the symlink is created.
  • `verbatim`: when true, store `sourcePath` in the link exactly as given instead of absolutizing it.

Returns:

  • `*SymbolicLink`: the link resource (resolved when created; the matched resource when already correct).
  • `*Receipt`: the compensation receipt for undo, or nil when no change was made.
  • `error`: non-nil on resource construction, archive, parent creation, symlink, or resolve failure.

+devlore:defaults verbatim=false

func (*Provider) Mkdir

func (p *Provider) Mkdir(
	activationRecord *op.ActivationRecord,
	path string,
	mode os.FileMode,
	user string,
	group string,
) (product *Directory, receipt *Receipt, err error)

Mkdir creates a directory (and any missing parents) at `path` with the given mode and ownership.

`user` and `group` each accept a name or a decimal id, and either may be empty to leave that side unchanged. When either is set they are applied via os.Chown to the leaf directory only — intermediate parents created by the call do NOT have their ownership changed, since their role is "existed before this call" rather than "created here."

Parameters:

  • `activationRecord`: the dispatch activation; its `Unit` stamps the produced *Directory's producerID.
  • `path`: the directory path to create.
  • `mode`: the os.FileMode applied to the leaf directory.
  • `user`: the owner applied to the leaf directory, by name or decimal uid; empty leaves it unchanged.
  • `group`: the group applied to the leaf directory, by name or decimal gid; empty leaves it unchanged.

Returns:

  • `*Directory`: the created directory resource, resolved; a nil receipt accompanies an already-existing directory.
  • `*Receipt`: the compensation receipt recording the creation boundary for undo.
  • `error`: non-nil when `path` exists as a non-directory, or on construction, mkdir, ownership, or resolve failure.

+devlore:defaults mode={{ umask 0o777 }}, user="", group=""

func (*Provider) Move

func (p *Provider) Move(
	activationRecord *op.ActivationRecord,
	sourcePath string,
	destinationPath string,
) (product Entry, receipt *Receipt, err error)

Move moves the entry at `sourcePath` to `destinationPath`, archiving any existing destination first.

Takes paths, not resources (step 23, ruling 2): a move renames — it never reads content. The destination product is minted as the moved entry's observed kind (the mutator is at execution time with the disk in hand), and the source identity rides the receipt so compensation can move the entry back. The destination's parents are created when absent. When an entry already exists at `destinationPath` it is archived for compensation; a failed rename attempts to restore that archived destination before returning the error.

Parameters:

  • `activationRecord`: the dispatch activation; its `Unit` stamps the produced Entry's producerID.
  • `sourcePath`: the path of the entry to move.
  • `destinationPath`: the path to move the entry to.

Returns:

  • `Entry`: the destination resource, minted as the source's observed kind, resolved.
  • `*Receipt`: the compensation receipt recording the source and any archived destination for undo.
  • `error`: non-nil when the source does not exist, or on construction, write preparation, rename, or resolve failure.

func (*Provider) Name

func (p *Provider) Name(path string) string

Name returns the last element of `path` (a file or directory name) via slashpath.Base.

Slash form, not OS-native: these helpers are a projected Starlark surface, and a path is a slash-form language on every platform — the same contract as io/fs and the canonical fsroot.Path rel form. filepath.Base would answer `\` for `/` on Windows, making a pure string operation platform-dependent.

Parameters:

  • `path`: the path whose last element is returned.

Returns:

  • `string`: the last path element, in slash form.

func (*Provider) Observe

func (p *Provider) Observe(resource Entry) (*Observation, error)

Observe captures the runtime-observed state of `resource` as an *Observation.

Stats the file at `resource.SourcePath`. When the file exists, the Observation carries the stat-derived metadata (`Size`, `Mode`, `ModTime`, `Inode`, `Device`) with `Exists` set to true. When the file does not exist (`os.ErrNotExist`), the Observation carries zero metadata with `Exists` set to false — not-exist is a valid observation outcome, not an error. Any other stat failure returns nil and the underlying error.

Parameters:

  • `resource`: the Entry whose current filesystem state to observe — observation minting is resource-coupled (step 23, ruling 2), and any taxonomy variant may be observed.

Returns:

  • `*Observation`: the constructed observation; never nil on a nil-error return.
  • `error`: any stat failure other than not-exist.

func (*Provider) Parent

func (p *Provider) Parent(path string) string

Parent returns the directory containing the file at `path` via slashpath.Dir.

Slash form, not OS-native — see Provider.Name for why.

Parameters:

  • `path`: the path whose containing directory is returned.

Returns:

  • `string`: the parent directory path, in slash form.

func (*Provider) ReadBytes

func (p *Provider) ReadBytes(resource *Regular) (product []byte, err error)

ReadBytes returns the contents of the file `resource` as bytes.

Parameters:

  • `resource`: the *Regular to read — a content read, so the parameter is the resource (step 23, ruling 2).

Returns:

  • `[]byte`: the file contents.
  • `error`: non-nil on read failure.

func (*Provider) ReadText

func (p *Provider) ReadText(resource *Regular) (product string, err error)

ReadText returns the contents of the file `resource` as text.

Parameters:

  • `resource`: the *Regular to read — a content read, so the parameter is the resource (step 23, ruling 2).

Returns:

  • `string`: the file contents.
  • `error`: non-nil on read failure.

func (*Provider) Remove

func (p *Provider) Remove(
	activationRecord *op.ActivationRecord,
	path string,
	prune bool,
	boundary string,
) (product Entry, receipt *Receipt, err error)

Remove deletes the file or empty directory at `path`, archiving it for compensation.

Takes a path (step 23, ruling 2) and discharges the delete invariants itself (ruling 3): the entry is interned via its Discover constructor as the observed kind (termination, not production — no producer stamp), moved to the recovery site, and its catalog entry marked op.Gone on success. A non-existent target is a no-op (nil product, nil receipt, nil error). A non-empty directory is an error — use Provider.RemoveAll for recursive deletion. When `prune` is set, now-empty parents up to `boundary` are removed.

Parameters:

  • `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
  • `path`: the path of the entry to delete.
  • `prune`: whether to remove now-empty parent directories up to `boundary`.
  • `boundary`: the path at which parent pruning stops; empty prunes to the scoped root.

Returns:

  • `Entry`: always nil — Remove produces no resource.
  • `*Receipt`: the compensation receipt recording the recovery archive for undo.
  • `error`: non-nil when the target is a non-empty directory, or on stat or archive failure.

func (*Provider) RemoveAll

func (p *Provider) RemoveAll(
	activationRecord *op.ActivationRecord,
	path string,
	prune bool,
	boundary string,
) (product Entry, receipt *Receipt, err error)

RemoveAll removes `resource` and any children it contains, archiving the subtree for compensation.

Unlike Provider.Remove, a non-empty directory is removed recursively. Takes a path and discharges the delete invariants (step 23, rulings 2 and 3): the entry is interned via its Discover constructor as the observed kind, moved to the recovery site, and its catalog entry marked op.Gone on success. A non-existent target is a no-op. When `prune` is set, now-empty parents up to `boundary` are removed afterward.

Parameters:

  • `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
  • `path`: the path of the entry to remove recursively.
  • `prune`: whether to remove now-empty parent directories up to `boundary`.
  • `boundary`: the path at which parent pruning stops; empty prunes to the scoped root.

Returns:

  • `Entry`: always nil — RemoveAll produces no resource.
  • `*Receipt`: the compensation receipt recording the recovery archive for undo.
  • `error`: non-nil on archive failure.

func (*Provider) Root

func (p *Provider) Root() string

Root returns the root path of the file-system scope, or the empty string when no root is set.

Returns:

  • `string`: the scoped root path, or "" when the session has no root.
func (p *Provider) Unlink(
	activationRecord *op.ActivationRecord,
	path string,
	prune bool,
	boundary string,
) (product Entry, receipt *Receipt, err error)

Unlink removes the symlink at `path`, archiving it for compensation.

Takes a path and discharges the delete invariants (step 23, rulings 2 and 3): the link is interned via DiscoverSymbolicLink (the kind is fixed by Unlink's own semantics), moved to the recovery site, and its catalog entry marked op.Gone on success. A non-existent target is a no-op. A target that exists but is not a symlink is an error. When `prune` is set, now-empty parents up to `boundary` are removed afterward.

Parameters:

  • `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
  • `path`: the path of the symlink to remove.
  • `prune`: whether to remove now-empty parent directories up to `boundary`.
  • `boundary`: the path at which parent pruning stops; empty prunes to the scoped root.

Returns:

  • `Entry`: always nil — Unlink produces no resource.
  • `*Receipt`: the compensation receipt recording the recovery archive for undo.
  • `error`: non-nil when the target exists but is not a symlink, or on stat or archive failure.

func (*Provider) WalkTree

func (p *Provider) WalkTree(
	activationRecord *op.ActivationRecord,
	root *Directory,
	fn Reducer,
	includeGitignored bool,
) (product any, stack *op.RecoveryStack, err error)

WalkTree performs a depth-first traversal of `root`, folding each entry through `fn`.

WalkTree is a discovery operation — the walker observes existing filesystem entries; it does not produce them. The Resources it interns into the catalog are discovered, not authored, so they carry no `producerID` stamp from this method. Gitignored entries are skipped unless `includeGitignored` is set; the `.git` directory is always skipped.

Parameters:

  • `activationRecord`: the dispatch activation (the required floor for compensable actions — step 27).
  • `root`: the *Directory to traverse — a content read of the tree, so the parameter is the resource (step 23, ruling 2).
  • `fn`: the Reducer invoked for each entry, threading an accumulator and the recovery stack.
  • `includeGitignored`: when false, entries matched by gitignore rules are skipped.

Returns:

  • `any`: the final accumulator value returned by the last `fn` invocation.
  • `*op.RecoveryStack`: the recovery stack accumulated during the walk, for compensation.
  • `error`: non-nil on tracker construction, stat, or any error returned by `fn`.

+devlore:defaults includeGitignored=false

func (*Provider) WriteBytes

func (p *Provider) WriteBytes(
	activationRecord *op.ActivationRecord,
	destinationPath string,
	content string,
	mode os.FileMode,
	user string,
	group string,
) (product *Regular, receipt *Receipt, err error)

WriteBytes writes inline byte `content` to a file at `destinationPath` with the given mode and ownership.

`user` and `group` each accept a name or a decimal id, and either may be empty to leave that side unchanged. When either is set they are applied via os.Chown after the file is written. Any existing file is archived for compensation before the write.

Parameters:

  • `activationRecord`: the dispatch activation; its `Unit` stamps the produced *Regular's producerID.
  • `destinationPath`: the path of the file to write.
  • `content`: the bytes to write, carried as a string.
  • `mode`: the os.FileMode applied to the written file.
  • `user`: the owner, by name or decimal uid; empty leaves the owner unchanged.
  • `group`: the group, by name or decimal gid; empty leaves the group unchanged.

Returns:

  • `*Regular`: the written resource.
  • `*Receipt`: the compensation receipt for undo.
  • `error`: non-nil on construction or write failure.

+devlore:defaults mode={{ umask 0o666 }}, user="", group=""

func (*Provider) WriteFile

func (p *Provider) WriteFile(
	activationRecord *op.ActivationRecord,
	targetPath string,
	src io.Reader,
	mode os.FileMode,
) (product *Regular, receipt *Receipt, err error)

WriteFile creates or updates the file at `targetPath` by streaming `src` to disk.

Any displaced content is archived for compensation. It is the exported form of the streaming write core: bytes flow through io.Copy (constant memory, and the kernel copy_file_range/sendfile fast path when `src` is an *os.File), and any content already at `targetPath` is archived to op.RecoverySite before the overwrite. Takes a path (step 23, ruling 2) and mints the *Regular product internally with the activation's producer stamp. WriteFile applies no ownership change (callers needing `user` / `group` use Provider.WriteText / Provider.WriteBytes). The returned *Receipt names Provider.CompensateFileMutation as its undo.

Parameters:

  • `activationRecord`: the dispatch activation; its `Unit` stamps the produced *Regular's producerID.
  • `targetPath`: the path of the file to write.
  • `src`: the byte source, streamed once via io.Copy without seeking or re-reading.
  • `mode`: the os.FileMode applied to the written file.

Returns:

  • `*Regular`: the written resource.
  • `*Receipt`: the self-describing compensation receipt naming Provider.CompensateFileMutation as its undo.
  • `error`: non-nil on construction, archive, or write failure.

func (*Provider) WriteText

func (p *Provider) WriteText(
	activationRecord *op.ActivationRecord,
	destinationPath string,
	content string,
	mode os.FileMode,
	user string,
	group string,
) (product *Regular, receipt *Receipt, err error)

WriteText writes inline text `content` to a file at `destinationPath` with the given mode and ownership.

`user` and `group` each accept a name or a decimal id, and either may be empty to leave that side unchanged. When either is set they are applied via os.Chown after the file is written. Any existing file is archived for compensation before the write.

Parameters:

  • `activationRecord`: the dispatch activation; its `Unit` stamps the produced *Regular's producerID.
  • `destinationPath`: the path of the file to write.
  • `content`: the text to write.
  • `mode`: the os.FileMode applied to the written file.
  • `user`: the owner, by name or decimal uid; empty leaves the owner unchanged.
  • `group`: the group, by name or decimal gid; empty leaves the group unchanged.

Returns:

  • `*Regular`: the written resource.
  • `*Receipt`: the compensation receipt for undo.
  • `error`: non-nil on construction or write failure.

+devlore:defaults mode={{ umask 0o666 }}, user="", group=""

type Receipt

type Receipt struct {
	op.ReceiptBase
	// contains filtered or unexported fields
}

Receipt holds the file-specific compensation state that the recovery system needs to undo a compensable forward call.

The embedded op.ReceiptBase carries the affected Resource whose identity is preserved across compensation, and an opaque op.ReceiptBase.TransactionID that op.RecoverySite interprets as the recovery key when restoring archived bytes. SourcePath always reflects the file's true home — the location compensation will write back to.

The optional boundary Resource marks the edge between the existing file system state and the subtree the forward action created. Compensation walks toward `boundary` and stops at it (exclusive). Provider.Mkdir, for example, sets `boundary` to the nearest pre-existing ancestor of its target directory so [Provider.CompensateMkdir] knows where to halt the upward removal walk. Methods that do not need a transactional anchor leave boundary nil.

The optional source Resource records the original location for move-like operations.

The optional recoveryDigest records the digest of the archived bytes at archive time. Compensation re-hashes the recovery archive and compares against this stored value to detect tampering of the recovery store between the forward action and compensation. Empty when no archive was made (recoveryID is also empty in that case).

func NewReceipt

func NewReceipt(spec *ReceiptSpec) *Receipt

NewReceipt builds a *Receipt from a populated *ReceiptSpec.

The receipt declares its undo at construction: it names [compensateFileMutationAction] as its compensating action (so Provider.CompensateFileMutation inverts it regardless of which method or dispatcher created it) and copies the spec's kind and optional boundary / recovery / source. The transactionID is minted later at op.ReceiptBase.Commit.

Parameters:

  • `spec`: the populated receipt spec; build it with NewReceiptSpec and its With* methods.

Returns:

  • `*Receipt`: the constructed receipt.

func (*Receipt) Boundary

func (r *Receipt) Boundary() Entry

Boundary returns the transactional boundary Entry supplied at construction, or nil if none was set.

Compensation methods read this value to bound their cleanup walk: any walk that would step past boundary (an upward walk reaching it, or a downward walk descending into it) must halt. A nil boundary signals that the forward action did not record a creation subtree and the compensation method has no boundary-driven cleanup to perform.

Returns:

  • `Entry`: the boundary supplied at construction, or nil for receipts built without one.

func (*Receipt) Kind

func (r *Receipt) Kind() MutationKind

Kind returns the MutationKind this receipt records, or "" when unset.

Returns:

  • `MutationKind`: the recorded mutation kind.

func (*Receipt) MarshalJSON

func (r *Receipt) MarshalJSON() ([]byte, error)

MarshalJSON encodes the receipt's compensation state as JSON — the resource, boundary, and source catalog ids plus the transaction id and recovery key/digest.

Delegates to Receipt.MarshalYAML for the serialized-shape value, then runs json.Marshal over it.

Returns:

func (*Receipt) MarshalYAML

func (r *Receipt) MarshalYAML() (any, error)

MarshalYAML returns the receipt's compensation state as an anonymous struct value the YAML encoder serializes.

This is the `receipt` subfield the recovery stack embeds for a resource receipt: resource, boundary, and source are emitted as catalog **ids** (a URI is not a unique identity — a shadowed generation shares its URI), alongside the transaction id, the recovery key/digest, and the mutation kind. The base execution state (`unit_id`/`action`/`result`/`status`) rides the stack-owned envelope, so it is not repeated here; resume resolves the ids via op.ResourceCatalog.Lookup in Receipt.RestoreEncoded. Both `json:` and `yaml:` tags ride every field so the value flows through either encoder via Receipt.MarshalJSON.

Returns:

  • `any`: the populated anonymous struct for the YAML encoder to walk.
  • `error`: nil under normal conditions.

func (*Receipt) RecoveryDigest

func (r *Receipt) RecoveryDigest() op.Digest

RecoveryDigest returns the digest of the bytes archived under Receipt.RecoveryID at archive time. The zero op.Digest value indicates no digest was captured (typically when nothing was archived).

Compensation methods read this value to verify the recovery archive's integrity before restoration: re-hash the archive's current bytes, compare against the stored digest, error on mismatch (the archive was tampered with between the forward action and compensation).

Returns:

  • `op.Digest`: the captured digest, or the zero value when none was set.

func (*Receipt) RecoveryID

func (r *Receipt) RecoveryID() string

RecoveryID returns the recovery ID for the file overwritten at the destination, or an empty string if none.

Returns:

  • `string`: the recovery ID.

func (*Receipt) RestoreEncoded

func (r *Receipt) RestoreEncoded(
	runtimeEnvironment *op.RuntimeEnvironment,
	base op.ReceiptData,
	fields map[string]any,
) error

RestoreEncoded reconstructs the receipt from its codec-decoded envelope, resolving its resource id references against the runtime environment's rehydrated ledger.

It is the op.Receipt.RestoreEncoded override for file receipts. The recovery stack already decoded the envelope — through whichever codec read the trace — so this consumes decoded values, never bytes: `base` carries the execution state and `fields` the id-reference sub-field. It resolves `resource_id`, `boundary_id`, and `source_id` via op.ResourceCatalog.Lookup (the ledger having been rehydrated first), seeds the base via op.NewReceiptBase + op.ReceiptBase.Restore, and restores the recovery key and digest. Resolving by id (not URI) pins the exact generation the receipt captured, even after the URI was shadowed by a later one.

Parameters:

  • `runtimeEnvironment`: the resume environment; its catalog must already hold the saved generations.
  • `base`: the codec-decoded base execution state.
  • `fields`: the receipt's id-reference sub-field, decoded to a format-neutral map.

Returns:

  • `error`: a missing catalog. The envelope arrives post-op.LoadTrace — checksum-verified — so an unresolved id or malformed field is a serialization bug and panics (docs/architecture/5-graph-trace-integrity.md).

func (*Receipt) Source

func (r *Receipt) Source() Entry

Source returns the original location Entry for move-like operations, or nil if none was set.

Returns:

  • `Entry`: the source resource.

type ReceiptSpec

type ReceiptSpec struct {
	// contains filtered or unexported fields
}

ReceiptSpec is the fluent builder for a *Receipt, mirroring the op.NodeSpec / op.NewNode shape used across the framework. The required identity — the affected Resource and the MutationKind — is supplied to NewReceiptSpec; optional compensation state (boundary, recovery, source) is added through the With* methods. Hand a populated spec to NewReceipt.

func NewReceiptSpec

func NewReceiptSpec(resource Entry, kind MutationKind) *ReceiptSpec

NewReceiptSpec returns a *ReceiptSpec for a `kind` mutation of `resource`, ready for optional With* population.

Parameters:

  • `resource`: the Entry affected by the compensable forward method call.
  • `kind`: the MutationKind the receipt records.

Returns:

  • `*ReceiptSpec`: the spec with its required identity populated.

func (*ReceiptSpec) WithBoundary

func (s *ReceiptSpec) WithBoundary(boundary Entry) *ReceiptSpec

WithBoundary records the transactional boundary — the nearest pre-existing ancestor a create walks back to during compensation — and returns the spec for chaining.

Parameters:

  • `boundary`: the existing-state edge; compensation stops at it (exclusive).

Returns:

  • `*ReceiptSpec`: the receiver, for chaining.

func (*ReceiptSpec) WithRecovery

func (s *ReceiptSpec) WithRecovery(recoveryID string, digest op.Digest) *ReceiptSpec

WithRecovery records the recovery archive of the displaced content and returns the spec for chaining.

A non-UUID `recoveryID` clears the id (a malformed key cannot name an archive); recovery keys produced by op.RecoverySite are always valid UUIDs, so this matches the prior construction, whose parse error was discarded.

Parameters:

  • `recoveryID`: the op.RecoverySite key for the archived bytes, as a UUID string.
  • `digest`: the digest of those bytes, captured at archive time for tamper detection.

Returns:

  • `*ReceiptSpec`: the receiver, for chaining.

func (*ReceiptSpec) WithSource

func (s *ReceiptSpec) WithSource(source Entry) *ReceiptSpec

WithSource records the original location for a move and returns the spec for chaining.

Parameters:

  • `source`: the move's origin Entry, to which compensation moves the file back.

Returns:

  • `*ReceiptSpec`: the receiver, for chaining.

type Reducer

type Reducer func(initial any, entry Entry, relativePath string, stack *op.RecoveryStack) (result any, err error)

Reducer folds one filesystem entry into an accumulator during a Provider.WalkTree traversal.

WalkTree calls the Reducer once per discovered entry, threading the prior `result` back in as `initial` so the final return value is the fold over the whole tree. The recovery `stack` is available for recording compensation.

Parameters:

  • `initial`: the accumulator returned by the previous invocation (nil on the first call).
  • `entry`: the Entry for the current filesystem entry, minted as its observed kind.
  • `relativePath`: the entry's path relative to the walk root.
  • `stack`: the *op.RecoveryStack for recording compensation actions.

Returns:

  • `any`: the updated accumulator, threaded into the next invocation.
  • `error`: non-nil to abort the traversal.

type Regular

type Regular struct {
	Resource
}

Regular is the taxonomy variant asserting that its path names a regular file (phase-8 step 23).

The kind is declared intent, never stat-assigned (ruling 1): planning is offline, so the assertion is verified at use rather than at construction — Regular.Digest and Regular.Etag observe the disk with lstat semantics and error with a kind mismatch when the entry is anything else (ruling 5e). Identity is the embedded Resource (URI + SourcePath); runtime-observed metadata lives on *Observation, exactly as for the base.

func DiscoverRegular

func DiscoverRegular(runtimeEnvironment *op.RuntimeEnvironment, value any) (*Regular, error)

DiscoverRegular registers a file.Regular via op.ResourceCatalog.Discover without claiming production.

The discovery counterpart of NewRegular: no producer is stamped, so no unit reference is taken. Nil-Catalog tolerance returns the unlinked candidate.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `value`: a string file path or file URI.

Returns:

  • `*Regular`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, the input violates RFC 8089 when in file URI form, the catalog's strict assertions fail, or the URI's existing entry is another kind.

func NewRegular

func NewRegular(runtimeEnvironment *op.RuntimeEnvironment, producerID string, value any) (*Regular, error)

NewRegular constructs a file.Regular and claims production via op.ResourceCatalog.GetOrCreate.

Use NewRegular from a producer dispatch context; the returned Regular is the canonical catalog entry, stamped with the given `producerID` when non-empty. A catalog entry already claimed under a different kind for the same URI is an error — cross-kind plan conflicts surface at the earliest moment. Nil-Catalog tolerance: the candidate is returned unlinked when no catalog is present.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `producerID`: the producing caller's id (`activationRecord.CallerID` — a unit id under graph dispatch, a starlark call-site under script dispatch), or "" for caller-less dispatch (an empty producer stamp).
  • `value`: a string file path or file URI.

Returns:

  • `*Regular`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, the input violates RFC 8089 when in file URI form, the catalog's strict assertions fail, or the URI's existing entry is another kind.

func (*Regular) CanConvertFrom

func (*Regular) CanConvertFrom(source reflect.Type) bool

CanConvertFrom reports whether `source` can be projected into a *Regular via Regular.ConvertFrom.

The variant's own probe for the framework's op.TargetConverter contract — defined directly (not promoted from the embedded base) because the cheap-probe contract calls it against a nil-or-zero `*Regular` receiver, and a promoted method would dereference the nil receiver to reach the embedded base. Today's accepted source shape is `string`, interpreted as a filesystem path under the active fsroot.

Parameters:

  • `source`: the candidate source type to test.

Returns:

  • `bool`: true when `source` is `string`.

func (*Regular) ConvertFrom

func (*Regular) ConvertFrom(value any) (any, error)

ConvertFrom projects `value` into a fresh *Regular.

Mirrors Resource.ConvertFrom: the returned value carries the path under SourcePath but is NOT catalog-interned at this layer; receiving provider methods intern via their own NewRegular/DiscoverRegular path.

Parameters:

  • `value`: the source value; must be `string`.

Returns:

  • `any`: the constructed unlinked *Regular.
  • `error`: non-nil when `value` is not a `string`.

func (*Regular) Digest

func (r *Regular) Digest() (op.Digest, error)

Digest returns the honest content hash: sha256 of the file's bytes, streamed (no full-file allocation).

Always fresh: the disk is observed at call time. The entry itself must be a regular file — the kind check uses lstat semantics, so a symlink pointing at a regular file is kind symbolic-link, not kind regular — and any other observed kind errors with a kind mismatch (step 23, ruling 5e): the plan asserted one kind, the disk shows another.

Returns:

  • `op.Digest`: sha256 algorithm with 32 raw bytes.
  • `error`: an lstat error, a kind mismatch, or any read error.

func (*Regular) Equal

func (r *Regular) Equal(other any) bool

Equal reports whether `r` and `other` identify the same regular-file resource.

Strict equality mirroring Resource.Equal: `other` must be a *file.Regular — the same URI held by another kind (or by the catch-all base) does not match. Once the type check passes, URI comparison is delegated to op.ResourceBase.Equal.

Parameters:

  • `other`: the value to compare against; may be `any`, including nil or a non-Regular.

Returns:

  • `bool`: true if `other` is a *file.Regular with the same URI as `r`.

func (*Regular) Etag

func (r *Regular) Etag() (string, error)

Etag returns the inexpensive stat-derived change-detection token for the regular file.

Always fresh: the disk is observed at call time with lstat semantics, and a kind other than regular file errors with a kind mismatch (step 23, ruling 5e). The token is the shared stat-tuple form: a sha256 of (size, mtime_ns, ino) packed little-endian, encoded as lowercase hex.

Returns:

  • `string`: lowercase hex sha256 of the packed stat tuple.
  • `error`: an lstat error or a kind mismatch.

func (*Regular) String

func (r *Regular) String() string

String returns a debug-oriented single-line representation of the regular-file resource.

Returns:

  • `string`: `file.Regular{uri=<URI>, source_path=<path>}`.

func (*Regular) UnmarshalJSON

func (r *Regular) UnmarshalJSON(data []byte) error

UnmarshalJSON populates the receiver from a JSON-encoded string (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant — defined directly so rehydration rebuilds a *Regular, never a half-filled embedded base.

Parameters:

  • `data`: JSON-encoded string containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the JSON does not decode as a string, or resource construction fails.

func (*Regular) UnmarshalText

func (r *Regular) UnmarshalText(text []byte) error

UnmarshalText populates the receiver from raw UTF-8 bytes containing a file path or file URI.

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant.

Parameters:

  • `text`: UTF-8 bytes containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing or resource construction fails.

func (*Regular) UnmarshalYAML

func (r *Regular) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML populates the receiver from a YAML scalar (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant.

Parameters:

  • `unmarshal`: callback supplied by the YAML decoder that projects the current node into the given target.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the YAML node does not decode as a string, or resource construction fails.

type Resource

type Resource struct {
	op.ResourceBase

	// SourcePath is the canonicalized absolute path on the disk. Set at construction by [buildCandidate] (which routes
	// the input through `RuntimeEnvironment.Root.NewPath`); rebound to the live execution fsroot by [Resource.Resolve]
	// when the run-time fsroot differs from the construction-time fsroot.
	SourcePath fsroot.Path
}

Resource represents a handle to a file on the disk identified by its path.

Resource carries identity only: the URI (derived from the absolute path) and the fsroot.Path handle. Runtime-observed state — size, mode, mod-time, inode, device, existence — lives on a separate *Observation minted by Provider.Observe; the framework owns observation storage so a buggy provider cannot corrupt the catalog by mutating fields on a shared *Resource pointer.

func (*Resource) Addressing

func (r *Resource) Addressing() op.AddressingMode

Addressing reports that file.Resource is location-keyed.

Identity is the path on the disk, and bytes at that path are mutable. The catalog uses op.AddressingLocation semantics. Content drift triggers shadow chains, not new URIs.

Returns:

func (*Resource) CanConvertFrom

func (*Resource) CanConvertFrom(source reflect.Type) bool

CanConvertFrom reports whether `source` can be projected into a *Resource via Resource.ConvertFrom.

Opts the file Resource into the framework's op.TargetConverter contract: the op.Convert cascade routes `source → *Resource` slot-fill through Resource.ConvertFrom at dispatch time (step 6 of the cascade), and [op.typesAreInterconvertible] consults the same probe at plan time so [op.Subgraph.mergeBubbled] does not flag a variable bound to both a `string` slot and a `*Resource` slot as a collision. Today's accepted source shape is `string` — interpreted as a filesystem path under the active fsroot. Other source shapes (file URI strings, Path values) can be added by extending this probe; the conversion body in Resource.ConvertFrom must accept the corresponding type.

Cheap-probe contract: this method is called against a nil-or-zero `*Resource` receiver by [op.typesAreInterconvertible] during plan-time bubble-up checks. It MUST NOT dereference receiver fields.

Parameters:

  • `source`: the candidate source type to test.

Returns:

  • `bool`: true when `source` is `string`.

func (*Resource) ConvertFrom

func (*Resource) ConvertFrom(value any) (any, error)

ConvertFrom projects `value` into a fresh *Resource.

Today's accepted shape is `string` — interpreted as a filesystem path under the active fsroot. The returned *Resource carries the path under Resource.SourcePath but is NOT catalog-interned at this layer; provider methods that receive the projected Resource are responsible for interning via their own taxonomy constructor path. This mirrors the inline `&Resource{SourcePath: fsroot.NewPath("", str)}` pattern used at writ adopt call sites pre-13.0(n) — the slot-fill cascade absorbs the pattern uniformly.

Parameters:

  • `value`: the source value; must be `string`.

Returns:

  • `any`: the constructed unlinked *Resource.
  • `error`: non-nil when `value` is not a `string`.

func (*Resource) ConvertTo

func (r *Resource) ConvertTo(target reflect.Type) (any, error)

ConvertTo projects this file resource into the given target Go type — the string form is the PATH.

Overrides op.ResourceBase.ConvertTo, whose baseline yields the canonical tag URI: a file resource's reachable string form is its absolute path (step 23, ruling 2 — the string turn feeds provider path parameters, and `op.ActionPlanner.Plan`'s location-immediate conversion is documented as producing path strings). The canonical URI remains the serialized identity via op.ResourceBase.MarshalText; only live-value projection is path-form. The taxonomy variants inherit this projection by promotion (always invoked on live values, never nil probes).

Parameters:

  • `target`: the destination Go type the caller wants to project the resource into.

Returns:

  • `any`: the absolute source path (as a Go string) when `target` is string.
  • `error`: non-nil if `target` is not a recognized conversion.

func (*Resource) Digest

func (r *Resource) Digest() (digest op.Digest, err error)

Digest returns the honest content hash: sha256 of the file's bytes, streamed (no full-file allocation).

Always fresh: opens and reads the file at call time. Errors with op.ErrUnimplemented for directories: the base file.Resource pre-dates the taxonomic split into Regular / Directory / SymbolicLink variants; directory hashing now lives on Directory.Digest (the Merkle root over the tree), so a directory is represented by a file.Directory.

Returns:

  • `op.Digest`: sha256 algorithm with 32 raw bytes.
  • `error`: a stat error, op.ErrUnimplemented for directories, or any read error.

func (*Resource) Equal

func (r *Resource) Equal(other any) bool

Equal reports whether `r` and `other` identify the same file resource.

Strict equality: `other` must be a *file.Resource (not merely an op.Resource with the same URI). Once the type check passes, URI comparison is delegated to op.ResourceBase.Equal. A cross-type URI collision (e.g., a file URI embedded in an appnet.Resource) fails at the type check rather than matching spuriously.

Parameters:

  • `other`: the value to compare against; may be `any`, including nil or a non-Resource.

Returns:

  • `bool`: true if `other` is a *file.Resource with the same URI as `r`.

func (*Resource) Etag

func (r *Resource) Etag() (string, error)

Etag returns an inexpensive stat-derived change-detection token.

Always fresh: stats the file at call time. The catalog uses Etag as an inexpensive signal that triggers a full Resource.Digest comparison. It is a sha256 of (size, mtime_ns, ino) packed into a little-endian byte array encoded as a lowercase hex string.

Returns:

  • `string`: lowercase hex sha256 of the packed stat tuple.
  • `error`: any stat error (file gone, permission denied, etc.).

func (*Resource) Exists

func (r *Resource) Exists() bool

Exists reports whether the file exists on disk at the time of the call.

Self-stat: performs a fresh stat at every call rather than reading any cached field. For richer metadata (size, mode, mod-time, etc.) call Provider.Observe which returns a *Observation.

Returns:

  • `bool`: true when the file exists; false when the stat returns os.ErrNotExist or any other error.

func (*Resource) IsDir

func (r *Resource) IsDir() bool

IsDir reports whether the file at this resource's path is a directory at the time of the call.

Self-stat. Returns false for any stat error (not-exist, permission denied, etc.) — callers that need to distinguish "missing" from "not a directory" should call Provider.Observe and check `obs.Exists` and `obs.Mode.IsDir()` separately.

Returns:

  • `bool`: true when the file exists and is a directory; false otherwise.

func (*Resource) Path

func (r *Resource) Path() fsroot.Path

Path returns the canonicalized absolute path handle on the disk.

The Entry accessor: mixed-kind holders (an Entry from enumeration or a walker callback) reach the path without asserting a concrete variant. The handle is the construction-time fsroot.Path; Resource.Resolve rebinds it to the live execution fsroot.

Returns:

  • `fsroot.Path`: the canonicalized absolute path handle.

func (*Resource) Resolve

func (r *Resource) Resolve() error

Resolve rebinds the source path to the execution fsroot and verifies the file exists.

The path is canonical from construction; rebinding updates Rel for confined I/O under the execution fsroot. If the file does not exist, Resolve returns nil — existence is observation, not identity, and `not-exist` is a valid observation outcome. Other stat failures (permission denied, I/O error) surface as errors.

Resolve does not populate any observation-shaped metadata on the Resource. Callers that need metadata call Provider.Observe to get an Observation value the framework can catalog.

Returns:

  • `error`: any stat error other than not-exist.

func (*Resource) String

func (r *Resource) String() string

String returns a debug-oriented single-line representation of the resource.

Suitable for log lines and IDE debug windows. Identity-only — observation-shaped data (size, mode, mod-time) is not on the Resource. Use Provider.Observe to capture observation values and log those alongside the Resource when needed.

Returns:

  • `string`: `file.Resource{uri=<URI>, source_path=<path>}`.

func (*Resource) UnmarshalJSON

func (r *Resource) UnmarshalJSON(data []byte) error

UnmarshalJSON populates the receiver from a JSON-encoded string (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; all domain-specific fields are then overwritten by the reconstructed resource.

Parameters:

  • `data`: JSON-encoded string containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the JSON does not decode as a string, or resource construction fails.

func (*Resource) UnmarshalText

func (r *Resource) UnmarshalText(text []byte) error

UnmarshalText populates the receiver from raw UTF-8 bytes containing a file path or file URI.

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; all domain-specific fields are then overwritten by the reconstructed resource.

Parameters:

  • `text`: UTF-8 bytes containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing or resource construction fails.

func (*Resource) UnmarshalYAML

func (r *Resource) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML populates the receiver from a YAML scalar (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; all domain-specific fields are then overwritten by the reconstructed resource.

Parameters:

  • `unmarshal`: callback supplied by the YAML decoder that projects the current node into the given target.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the YAML node does not decode as a string, or resource construction fails.
type SymbolicLink struct {
	Resource
}

SymbolicLink is the taxonomy variant asserting that its path names a symbolic link (phase-8 step 23).

The kind is declared intent, never stat-assigned (ruling 1): planning is offline, so the assertion is verified at use rather than at construction — SymbolicLink.Digest and SymbolicLink.Etag observe the disk with lstat semantics and error with a kind mismatch when the entry is anything else (ruling 5e). A dangling link is legal everywhere: the link is the resource, not its referent, which has its own resource identity. Identity is the embedded Resource (URI + SourcePath); runtime-observed metadata lives on *Observation.

func DiscoverSymbolicLink(runtimeEnvironment *op.RuntimeEnvironment, value any) (*SymbolicLink, error)

DiscoverSymbolicLink registers a file.SymbolicLink via op.ResourceCatalog.Discover without claiming production.

The discovery counterpart of NewSymbolicLink: no producer is stamped, so no unit reference is taken. Nil-Catalog tolerance returns the unlinked candidate.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `value`: a string file path or file URI.

Returns:

  • `*SymbolicLink`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, the input violates RFC 8089 when in file URI form, the catalog's strict assertions fail, or the URI's existing entry is another kind.
func NewSymbolicLink(
	runtimeEnvironment *op.RuntimeEnvironment,
	producerID string,
	value any,
) (*SymbolicLink, error)

NewSymbolicLink constructs a file.SymbolicLink and claims production via op.ResourceCatalog.GetOrCreate.

Use NewSymbolicLink from a producer dispatch context; the returned SymbolicLink is the canonical catalog entry, stamped with the given `producerID` when non-empty. A catalog entry already claimed under a different kind for the same URI is an error — cross-kind plan conflicts surface at the earliest moment. Nil-Catalog tolerance: the candidate is returned unlinked when no catalog is present.

Parameters:

  • `runtimeEnvironment`: the session runtime environment.
  • `producerID`: the producing caller's id (`activationRecord.CallerID` — a unit id under graph dispatch, a starlark call-site under script dispatch), or "" for caller-less dispatch (an empty producer stamp).
  • `value`: a string file path or file URI.

Returns:

  • `*SymbolicLink`: the canonical catalog entry (or the unlinked candidate when no catalog is present).
  • `error`: if `value` is not a string, the input violates RFC 8089 when in file URI form, the catalog's strict assertions fail, or the URI's existing entry is another kind.

func (*SymbolicLink) CanConvertFrom

func (*SymbolicLink) CanConvertFrom(source reflect.Type) bool

CanConvertFrom reports whether `source` can be projected into a *SymbolicLink via SymbolicLink.ConvertFrom.

The variant's own probe for the framework's op.TargetConverter contract — defined directly (not promoted from the embedded base) because the cheap-probe contract calls it against a nil-or-zero `*SymbolicLink` receiver, and a promoted method would dereference the nil receiver to reach the embedded base. Today's accepted source shape is `string`, interpreted as a filesystem path under the active fsroot.

Parameters:

  • `source`: the candidate source type to test.

Returns:

  • `bool`: true when `source` is `string`.

func (*SymbolicLink) ConvertFrom

func (*SymbolicLink) ConvertFrom(value any) (any, error)

ConvertFrom projects `value` into a fresh *SymbolicLink.

Mirrors Resource.ConvertFrom: the returned value carries the path under SourcePath but is NOT catalog-interned at this layer; receiving provider methods intern via their own NewSymbolicLink/DiscoverSymbolicLink path.

Parameters:

  • `value`: the source value; must be `string`.

Returns:

  • `any`: the constructed unlinked *SymbolicLink.
  • `error`: non-nil when `value` is not a `string`.

func (*SymbolicLink) Digest

func (r *SymbolicLink) Digest() (op.Digest, error)

Digest returns the honest content hash of the link itself: sha256 of the literal readlink target, never following.

A symbolic link IS a tiny file whose content is a path — hashing that content is the honest digest (step 23, ruling 5a). The target is taken verbatim from readlink (no cleaning, no absolutization): the link's content is what it is. A dangling link digests normally, and no cycle is possible because nothing is followed. The entry itself must be a symbolic link — any other observed kind errors with a kind mismatch (ruling 5e).

Returns:

  • `op.Digest`: sha256 algorithm with 32 raw bytes — the hash of the literal target path.
  • `error`: an lstat error, a kind mismatch, or a readlink failure.

func (*SymbolicLink) Equal

func (r *SymbolicLink) Equal(other any) bool

Equal reports whether `r` and `other` identify the same symbolic-link resource.

Strict equality mirroring Resource.Equal: `other` must be a *file.SymbolicLink — the same URI held by another kind (or by the catch-all base) does not match. Once the type check passes, URI comparison is delegated to op.ResourceBase.Equal.

Parameters:

  • `other`: the value to compare against; may be `any`, including nil or a non-SymbolicLink.

Returns:

  • `bool`: true if `other` is a *file.SymbolicLink with the same URI as `r`.

func (*SymbolicLink) Etag

func (r *SymbolicLink) Etag() (string, error)

Etag returns the inexpensive stat-derived change-detection token for the link inode itself.

Lstat-based (step 23, ruling 5b): the token reflects the link, not its referent, so a dangling link has a valid Etag. This fixes by construction the catch-all's latent defect — its Etag stats through `root.Stat`, which FOLLOWS symlinks, so a link's token reflected its referent and errored on a dangling link. A kind other than symbolic link errors with a kind mismatch (ruling 5e). The token is the shared stat-tuple form: a sha256 of (size, mtime_ns, ino) packed little-endian, encoded as lowercase hex.

Returns:

  • `string`: lowercase hex sha256 of the packed stat tuple of the link inode.
  • `error`: an lstat error or a kind mismatch.

func (*SymbolicLink) String

func (r *SymbolicLink) String() string

String returns a debug-oriented single-line representation of the symbolic-link resource.

Returns:

  • `string`: `file.SymbolicLink{uri=<URI>, source_path=<path>}`.

func (*SymbolicLink) UnmarshalJSON

func (r *SymbolicLink) UnmarshalJSON(data []byte) error

UnmarshalJSON populates the receiver from a JSON-encoded string (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant — defined directly so rehydration rebuilds a *SymbolicLink, never a half-filled embedded base.

Parameters:

  • `data`: JSON-encoded string containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the JSON does not decode as a string, or resource construction fails.

func (*SymbolicLink) UnmarshalText

func (r *SymbolicLink) UnmarshalText(text []byte) error

UnmarshalText populates the receiver from raw UTF-8 bytes containing a file path or file URI.

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant.

Parameters:

  • `text`: UTF-8 bytes containing the resource's URI or path.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing or resource construction fails.

func (*SymbolicLink) UnmarshalYAML

func (r *SymbolicLink) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML populates the receiver from a YAML scalar (a file path or file URI).

The caller pre-seeds the receiver's embedded op.ResourceBase with a valid op.RuntimeEnvironment before invoking this method; the whole receiver is then overwritten by the reconstructed variant.

Parameters:

  • `unmarshal`: callback supplied by the YAML decoder that projects the current node into the given target.

Returns:

  • `error`: non-nil if the RuntimeEnvironment is missing, the YAML node does not decode as a string, or resource construction fails.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL