file

package
v0.1.0-dev.20260825233553 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 25 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"
	Discover   op.ActionName = "file.discover"
	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"
	Resolve    op.ActionName = "file.resolve"
	Root       op.ActionName = "file.root"
	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

func NormalizePlanSpacePath

func NormalizePlanSpacePath(path string) (string, error)

NormalizePlanSpacePath renders an authored plan path into its canonical rel, or refuses it.

The git model (#584, ruled 2026-08-20; docs/architecture/4-resource-management.md §5.2): plan-space paths are a portable little language. `foo/bar` and `/foo/bar` both name rel `foo/bar` — the leading slash is the anchored spelling; machine-absoluteness is inexpressible in a plan (it arises only from the run's root choice). The refusals:

  • a volume or drive-letter spelling (`C:\x`, `C:/x`) — malformed plan input;
  • a UNC spelling (`//server/share`, `\\server\share`) — malformed plan input;
  • any backslash — plan space is slash-form on every platform; a backslash is a native-spelling leak;
  • a root-qualified spelling (`@name/…`) — reserved for the named multi-root design (#597);
  • a rel that escapes (`../…`) or names the root itself — intent confinement can never satisfy.

Parameters:

  • `path`: the authored plan path.

Returns:

  • `string`: the slash-canonical rel.
  • `error`: non-nil for every refusal above.

func NormalizeRuntimePath

func NormalizeRuntimePath(root fsroot.Dir, path string) (string, error)

NormalizeRuntimePath renders a run-computed path into the slash-canonical rel the run catalog speaks — the runtime dialect of the plan-space grammar (4-resource-management.md §5.7 rule 3, ruled 2026-08-22).

Rels normalize exactly as authored plan-space paths do, with the same refusals: escapes, the reserved @name spelling, the bare root, backslashes. The dialect's one addition: a machine-absolute input is interpretable here because the run's root is bound — machine-absoluteness arises only from the run's root choice (§5.2), and the discovery actions sit on the far side of that choice — so an absolute path under `root` rebases to its rel, and one outside `root` (another volume and UNC spellings included) refuses as a confinement violation.

Dialect sharpening, recorded at PR 3: on unix a leading slash is ambiguous between the plan-space anchored spelling and machine-absoluteness — at run time the machine reading wins (tools emit machine absolutes), so the two readings agree under the root and an out-of-root absolute refuses rather than silently confining. Authors of literals write bare rels.

Parameters:

  • `root`: the run's bound root.
  • `path`: the run-computed path in any spelling.

Returns:

  • `string`: the slash-canonical rel.
  • `error`: the dialect's refusal.

Types

type AnyKind

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

AnyKind is the taxonomy variant that asserts existence and nothing else — the unasserted claim (docs/plans/any-entry-claims.md, ruled 2026-08-23).

The kinded variants declare what must be at a path; AnyKind declines to, which is what a kind-indifferent operation needs: a move moves whatever is there, and the author should not have to name a kind the operation does not care about. Its assertion is exactly "some taxonomy entry exists at this rel" — a dangling symbolic link satisfies it (the link is there), a FIFO, socket, or device does not (the taxonomy has no variant for them).

An unasserted claim is in effect a promise to observe: the entry resolves to the variant the disk shows inside the op.Pendingop.Active transition, where the model first consults the disk. A claim that fails to activate stays an AnyKind — nothing was observed, so there is nothing to resolve to, and the op.Gone entry honestly records an unmet unasserted claim.

Identity is the embedded [resource] (URI + SourcePath), and the serialized intent row names this type — intent says *something must be here*; the trace says what was found.

func (*AnyKind) Addressing

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

Addressing reports that file.entry 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 (*AnyKind) BindRoot

func (r *AnyKind) BindRoot(root fsroot.Dir)

BindRoot re-binds this resource's location to `root`, rel-first — the activation binding (§5.5), driven from the executor's pre-flight resolve pass through the op.RootBinder seam.

Identity (the rel) is unchanged; the root becomes the run's; the native form derives. The environment re-base happens executor-side, so after binding every observation — existence, Etag, Digest, I/O — reads the run's world.

Parameters:

  • `root`: the run's bound fsroot.

func (*AnyKind) CanConvertFrom

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

CanConvertFrom reports whether `source` can be projected into a [*resource] via [entry.ConvertFrom].

Opts the file entry into the framework's op.TargetConverter contract: the op.Convert cascade routes `source → *resource` slot-fill through [entry.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 [entry.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 (*AnyKind) ConvertFrom

func (*AnyKind) 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 [entry.SourcePath] but is NOT catalog-interned at this layer; provider methods that receive the projected entry 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 (*AnyKind) ConvertTo

func (r *AnyKind) 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 (*AnyKind) Digest

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

Digest returns the honest content hash of whatever the disk holds, by delegating to the observed kind's implementation.

An unasserted claim still owes content identity: without this, an AnyKind entry would record no digest and silently lose drift detection for as long as it remains unresolved. The observed kind's own contract applies verbatim — a directory's digest rule, a link's target hash, a regular file's streamed sha256.

Returns:

  • `op.Digest`: the observed kind's digest.
  • `error`: an lstat failure, an unsupported entry kind, or the kind's own digest error.

func (*AnyKind) Equal

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

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

Strict equality: `other` must be a *file.entry (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-entry.

Returns:

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

func (*AnyKind) Etag

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

Etag returns the cheap change-detection token of whatever the disk holds, by delegating to the observed kind's implementation — the screen half of the same contract AnyKind.Digest serves.

Returns:

  • `string`: the observed kind's etag.
  • `error`: an lstat failure, an unsupported entry kind, or the kind's own etag error.

func (*AnyKind) Exists

func (r *AnyKind) Exists() bool

Exists reports whether ANY taxonomy entry exists at this resource's path — lstat, no follow, no kind assertion.

The permissive predicate, and deliberately not the kinded variants' lstat-plus-kind-test nor a following os.Stat: a dangling symbolic link must count as present (the link is the entry), and an entry the taxonomy has no variant for — a FIFO, socket, or device — must not, because no claim could legitimately resolve to it.

Returns:

  • `bool`: true when the path holds a regular file, a directory, or a symbolic link.

func (*AnyKind) IsDir

func (r *AnyKind) 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 (*AnyKind) MismatchesKind

func (r *AnyKind) MismatchesKind() bool

MismatchesKind reports whether the path holds an entry no taxonomy variant covers — a FIFO, a socket, a device (op.KindMismatcher).

Even the unasserted claim has a bound: it asserts *some taxonomy entry*, so a kind nothing could resolve to is a surprise rather than an absence, and a surprise is never tolerable.

Returns:

  • `bool`: true when an entry is there and no variant admits it.

func (*AnyKind) Path

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

Path returns the canonicalized absolute path handle on the disk.

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

Returns:

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

func (*AnyKind) Resolve

func (r *AnyKind) 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 entry. 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 (*AnyKind) ResolveKind

func (r *AnyKind) ResolveKind() (op.Resource, error)

ResolveKind returns the taxonomy variant the disk currently holds — the promise to observe, come due (4-resource-management.md; op.KindResolver).

An unasserted claim asserts existence and defers the kind; pre-flight's Pending → Active transition is where the model first looks, so it is where the deferral ends. The returned variant is freshly built and uninterned: the catalog stamps identity across the swap, because identity is the catalog's business, not this resource's.

Returns:

  • `op.Resource`: the observed-kind variant at this path.
  • `error`: an lstat failure, or an entry kind the taxonomy has no variant for.

func (*AnyKind) String

func (r *AnyKind) 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 entry. Use Provider.Observe to capture observation values and log those alongside the entry when needed.

Returns:

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

func (*AnyKind) UnmarshalJSON

func (r *AnyKind) 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 (*AnyKind) UnmarshalText

func (r *AnyKind) 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 (*AnyKind) UnmarshalYAML

func (r *AnyKind) 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 Directory

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

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) Addressing

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

Addressing reports that file.entry 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 (*Directory) BindRoot

func (r *Directory) BindRoot(root fsroot.Dir)

BindRoot re-binds this resource's location to `root`, rel-first — the activation binding (§5.5), driven from the executor's pre-flight resolve pass through the op.RootBinder seam.

Identity (the rel) is unchanged; the root becomes the run's; the native form derives. The environment re-base happens executor-side, so after binding every observation — existence, Etag, Digest, I/O — reads the run's world.

Parameters:

  • `root`: the run's bound fsroot.

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 [entry.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) ConvertTo

func (r *Directory) 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 (*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. Resource 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 [entry.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) Exists

func (r *Directory) Exists() bool

Exists reports whether a DIRECTORY exists at this resource's path — lstat plus kind test (kind-honest activation, ruled 2026-08-22; step 23 ruling 5e).

Kinds are lstat-strict: a regular file or a symbolic link at the path is not this resource, so a *Directory claim over one fails verification at the starting line — "claims are true when made" — rather than activating kind-blind and failing later at observation or I/O.

Returns:

  • `bool`: true when the path holds a directory; false on any lstat error or any other kind.

func (*Directory) IsDir

func (r *Directory) 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 (*Directory) MismatchesKind

func (r *Directory) MismatchesKind() bool

MismatchesKind reports whether the path holds an entry that is not a directory — the seam that separates a wrong-kind claim from an absent one (op.KindMismatcher).

Only a mismatch is intolerable: op.MissingResourcePolicyIgnore means "the goal already holds", which is true of absence and false of a surprise.

Returns:

  • `bool`: true when an entry is there and it is not a directory.

func (*Directory) Path

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

Path returns the canonicalized absolute path handle on the disk.

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

Returns:

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

func (*Directory) Resolve

func (r *Directory) 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 entry. 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 (*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 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 Resource,
	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 Resource 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,
	source Resource,
	backupSuffix string,
) (Resource, *Receipt, error)

Backup moves `source` to a timestamped backup location, delegating to Provider.Move.

The source is a consumed, claimed resource of any taxonomy kind — the surface's last unclaimed mutation source until 2026-08-23, when it joined its siblings. Backup renames rather than reading content, so the kind is the disk's business: whatever Provider.Move can move, Backup can back up.

Parameters:

  • `activationRecord`: the dispatch activation threaded to Provider.Move.
  • `source`: the entry to back up — a consumed, claimed resource.
  • `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:

  • `Resource`: 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) Discover

func (p *Provider) Discover(path string, kind ResourceKind, after op.OrderingEdge) (product Resource, err error)

Discover interns the entry at `path` — lstat, the entry itself, no follow — as a discovery: an observed runtime fact, no production claim (4-resource-management.md §5.7, ruled 2026-08-22).

The path is run-computed input — a promise's value, a literal naming a mid-run fact (an opaque command's side effect at a known path), or anything the conversion cascade renders to a string — normalized through the runtime dialect of the plan-space grammar (NormalizeRuntimePath): rels as authored, a machine-absolute under the bound root rebased to its rel, everything else refused. A file that must exist when the run starts is CLAIMED instead (§5.1 — pre-flight's verdict); discover is for facts that come into being mid-run.

`kind` is opt-in strictness (default `entry`): a specific kind must match the lstat-observed kind and the verdict lands at this node — kinds are lstat-strict, so a symbolic link to a regular file is kind symbolic-link and Provider.Resolve is the explicit follow. Stop-only: a missing target, a kind mismatch, or a grammar refusal is this action's error; there is no on_missing (an Ignore would hand nil promises downstream — the cost that had Skip dropped from the policy enum).

`after` is the pure ordering edge (ruled at PR 3/#611): bind an upstream invocation to sequence this discovery after it — only the promise edge matters, and the delivered value is discarded.

Parameters:

  • `path`: the run-computed path, in any spelling the runtime dialect admits.
  • `kind`: the asserted ResourceKind; `entry` (the default) admits any taxonomy kind.
  • `after`: an optional upstream invocation consumed solely as an ordering edge (op.OrderingEdge); nil means no edge.

Returns:

  • `Resource`: the discovered entry, interned Active in the catalog as its observed kind.
  • `error`: a grammar refusal, an lstat failure (including not-exist), a kind mismatch, or the catalog's verdict (a known-Gone entry does not re-discover).

+devlore:defaults kind=any, after=nil

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 []Resource, 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:

  • `[]Resource`: 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) ([]Resource, 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:

  • `[]Resource`: 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,
	source Resource,
	destinationPath string,
) (product Resource, receipt *Receipt, err error)

Move moves `source` to `destinationPath`, archiving any existing destination first.

**AnyKind kind moves.** The source is claimed as Resource, the taxonomy's interface, so an authored path claims as *AnyKind and resolves to whatever the disk holds at activation: a regular file, a directory and its subtree (a rename carries it whole), or a symbolic link — the link itself, never the entry it designates. The kind is the disk's business, not the author's; a move moves what is there.

The source is a consumed resource (mutation targets are resource-typed consumers — ruled 2026-08-20; a move destroys the source location): its claim enters the graph's catalog as required intent, and its catalog entry is marked op.Gone with the destroyer stamp on success. **A missing source fails** — there is no tolerance parameter, because moving something that is gone accomplishes nothing and a tolerated miss would hand downstream consumers a nil product, which is the pathology that had `Skip` dropped from op.MissingResourcePolicy. Under the fail-safe default the miss is unmet intent, caught by the consuming scope's pre-flight before this method is ever reached; the check here is the in-flight backstop for a source that vanishes between verification and dispatch.

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 Resource's producerID.
  • `source`: the entry to move — a consumed, claimed resource of any taxonomy kind.
  • `destinationPath`: the path to move the entry to.

Returns:

  • `Resource`: 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 is missing, 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 Resource) (*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 Resource 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,
	target Resource,
	prune bool,
	boundary string,
	onMissing op.MissingResourcePolicy,
) (product Resource, receipt *Receipt, err error)

Remove deletes the single entry at `target`, archiving it for compensation.

**AnyKind kind is removable.** The target is claimed as Resource, so an authored path claims as *AnyKind and resolves to whatever the disk holds: a regular file, an empty directory, or a symbolic link — the link itself, never the entry it designates, because the entry is discovered with lstat semantics and a removal never follows. The removal family splits by **blast radius, never by kind**, which is the standard library's split for the same reasons — os.Remove does not ask the kind either, trying unlink then rmdir, which is why Go never needed an Unlink.

A **non-empty directory is refused**: Provider.RemoveAll owns subtrees. The guard is authoring policy rather than a capability boundary — the destructive step is an archival rename, which would move a populated tree perfectly well — and it exists so that destroying a tree must be said rather than stumbled into. A symbolic link never reaches the guard, whatever it designates.

A missing target follows `onMissing`: stop (the default) fails the call, ignore records the no-op.

Parameters:

  • `activationRecord`: the dispatch activation; its caller stamps the op.Gone transition.
  • `target`: the entry to remove — a consumed, claimed resource of any taxonomy kind.
  • `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.
  • `onMissing`: the op.MissingResourcePolicy for an absent target; defaults to stop.

Returns:

  • `Resource`: always nil — a removal produces nothing.
  • `*Receipt`: the compensation receipt carrying the archived entry.
  • `error`: a missing target under stop, a non-empty directory, or an archive failure.

+devlore:defaults prune=false, boundary="", onMissing=stop

func (*Provider) RemoveAll

func (p *Provider) RemoveAll(
	activationRecord *op.ActivationRecord,
	target Resource,
	prune bool,
	boundary string,
	onMissing op.MissingResourcePolicy,
) (product Resource, receipt *Receipt, err error)

RemoveAll deletes the entry at `target` and everything beneath it — `rm -rf`, with undo.

Provider.Remove's unguarded sibling: same claim, same archival, same receipt, and the only difference is that a populated directory is removed rather than refused. Over a **symbolic link it removes the link**, never the tree the link designates — a follow is an explicit act (Provider.Resolve), and the archival rename cannot perform one even by accident. `shutil.rmtree` refuses this case outright rather than choose; that insurance is unnecessary here.

A missing target follows `onMissing`. Note the deliberate divergence from os.RemoveAll, which tolerates a missing path (that is `rm -rf`'s `-f`): the default here is stop, because fail-safe beats familiarity, and an author writes `on_missing="ignore"` to get the standard library's behavior.

Parameters:

  • `activationRecord`: the dispatch activation; its caller stamps the op.Gone transition.
  • `target`: the entry to remove — a consumed, claimed resource of any taxonomy kind.
  • `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.
  • `onMissing`: the op.MissingResourcePolicy for an absent target; defaults to stop.

Returns:

  • `Resource`: always nil — a removal produces nothing.
  • `*Receipt`: the compensation receipt carrying the archived subtree.
  • `error`: a missing target under stop, or an archive failure.

+devlore:defaults prune=false, boundary="", onMissing=stop

func (*Provider) Resolve

func (p *Provider) Resolve(path string, kind ResourceKind, after op.OrderingEdge) (product Resource, err error)

Resolve interns what the chain at `path` designates — stat, the full follow — as a discovery: the terminus entity, which is never a symbolic link (4-resource-management.md §5.7, ruled 2026-08-22).

The kernel resolves names implicitly at open; this model resolves designation explicitly at this node. The chain from `path` follows to its terminus; the terminus must lie within the run's root — confinement judges the FOLLOW, because a symbolic link is the disk's "../" — and the interned identity is the TERMINUS's rel: the designated entity, not the link. Resolving a non-link is the harmless identity follow. A dangling chain is this action's error, as is an escaping one. `kind` asserts the terminus's kind (default `entry`); asserting `symbolic_link` can never satisfy. `after` is the pure ordering edge, exactly as on Provider.Discover. Stop-only, like its sibling.

Parameters:

  • `path`: the run-computed path, in any spelling the runtime dialect admits.
  • `kind`: the asserted ResourceKind of the terminus; `entry` (the default) admits any taxonomy kind.
  • `after`: an optional upstream invocation consumed solely as an ordering edge (op.OrderingEdge); nil means no edge.

Returns:

  • `Resource`: the designated entry, interned Active in the catalog under the terminus's identity.
  • `error`: a grammar refusal, a dangling or escaping chain, a kind mismatch, or the catalog's verdict.

+devlore:defaults kind=any, after=nil

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 (*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() Resource

Boundary returns the transactional boundary Resource 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:

  • `Resource`: 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() Resource

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

Returns:

  • `Resource`: 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 Resource, kind MutationKind) *ReceiptSpec

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

Parameters:

  • `resource`: the Resource 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 Resource) *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 Resource) *ReceiptSpec

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

Parameters:

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

Returns:

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

type Reducer

type Reducer func(initial any, entry Resource, 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 Resource 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 {
	// contains filtered or unexported fields
}

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) Addressing

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

Addressing reports that file.entry 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 (*Regular) BindRoot

func (r *Regular) BindRoot(root fsroot.Dir)

BindRoot re-binds this resource's location to `root`, rel-first — the activation binding (§5.5), driven from the executor's pre-flight resolve pass through the op.RootBinder seam.

Identity (the rel) is unchanged; the root becomes the run's; the native form derives. The environment re-base happens executor-side, so after binding every observation — existence, Etag, Digest, I/O — reads the run's world.

Parameters:

  • `root`: the run's bound fsroot.

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 [entry.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) ConvertTo

func (r *Regular) 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 (*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 [entry.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) Exists

func (r *Regular) Exists() bool

Exists reports whether a REGULAR FILE exists at this resource's path — lstat plus kind test (kind-honest activation, ruled 2026-08-22; step 23 ruling 5e).

Kinds are lstat-strict: a symbolic link or a directory at the path is not this resource, so a *Regular claim over one fails verification at the starting line — "claims are true when made" — rather than activating kind-blind and failing later at observation or I/O.

Returns:

  • `bool`: true when the path holds a regular file; false on any lstat error or any other kind.

func (*Regular) IsDir

func (r *Regular) 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 (*Regular) MismatchesKind

func (r *Regular) MismatchesKind() bool

MismatchesKind reports whether the path holds an entry that is not a regular file — the seam that separates a wrong-kind claim from an absent one (op.KindMismatcher).

Only a mismatch is intolerable: op.MissingResourcePolicyIgnore means "the goal already holds", which is true of absence and false of a surprise.

Returns:

  • `bool`: true when an entry is there and it is not a regular file.

func (*Regular) Path

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

Path returns the canonicalized absolute path handle on the disk.

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

Returns:

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

func (*Regular) Resolve

func (r *Regular) 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 entry. 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 (*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 interface {
	op.Resource

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

Resource is this provider's resource type — the sealed interface the taxonomy's variants implement.

Every provider names its resource type `Resource`; file's is an interface rather than a struct because file is the one provider with a kind axis. 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, kind-indifferent mutation — accept or return a Resource rather than a concrete variant. Contexts whose semantics fix the kind use the variant (*Regular, *Directory, *SymbolicLink) directly; *AnyKind is the variant that asserts existence without asserting kind.

func DiscoverAnyKind

func DiscoverAnyKind(runtimeEnvironment *op.RuntimeEnvironment, value any) (Resource, error)

DiscoverAnyKind registers a file.AnyKind via op.ResourceCatalog.Discover without claiming production — the constructor plan-time claiming and rehydration both key on.

AnyKind has no producing counterpart: a product is minted as its observed kind (the producer is at execution time with the disk in hand), so nothing ever produces an unasserted entry. Nil-catalog tolerance returns the unlinked candidate.

Parameters:

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

**Returns the taxonomy interface, not `*AnyKind`** — the one constructor that does, and deliberately. An unasserted claim is satisfied by whatever entry already stands for its identity, and a kinded entry asserts more, so it keeps the ledger slot (the collision rule: one rel, one identity, the stricter assertion wins). A constructor that promised `*AnyKind` would have to fail in exactly the case the rule says should succeed.

Returns:

  • `Resource`: the canonical catalog entry — a fresh *AnyKind when the identity is unclaimed, the existing kinded entry when it is not, or the unlinked candidate when no catalog is present.
  • `error`: if `value` is not a string, or the catalog's strict assertions fail.

type ResourceKind

type ResourceKind int

ResourceKind is the kind a discovery or resolution asserts (4-resource-management.md §5.7, ruled 2026-08-22).

The `entry` zero value is the permissive default: the short spelling accepts whatever kind the disk holds, and asserting a specific kind is opt-in strictness whose verdict sharpens at the asserting action's own node. Kinds are lstat-strict (step 23, ruling 5e): a symbolic link to a regular file is kind symbolic-link, never regular — Provider.Resolve is the explicit follow.

const (
	// ResourceKindAny is the zero value and the default — permissive: any taxonomy kind is accepted.
	ResourceKindAny ResourceKind = 0

	// ResourceKindRegular asserts a regular file.
	ResourceKindRegular ResourceKind = 1

	// ResourceKindDirectory asserts a directory.
	ResourceKindDirectory ResourceKind = 2

	// ResourceKindSymbolicLink asserts a symbolic link — the link itself, lstat semantics.
	ResourceKindSymbolicLink ResourceKind = 3
)

func (ResourceKind) MarshalJSON

func (k ResourceKind) MarshalJSON() ([]byte, error)

MarshalJSON serializes the kind as its canonical lowercase string — a document carries "regular", never a bare ordinal (the typed-value rule: no value degrades to its least-typed rendering).

Returns:

  • `[]byte`: the JSON string form.
  • `error`: any error from the underlying marshal.

func (ResourceKind) MarshalYAML

func (k ResourceKind) MarshalYAML() (any, error)

MarshalYAML serializes the kind as its canonical lowercase string scalar.

Returns:

  • `any`: the string form.
  • `error`: always nil.

func (ResourceKind) String

func (k ResourceKind) String() string

String returns the canonical lowercase rendering of the kind.

Returns:

  • `string`: "any", "regular", "directory", or "symbolic_link".

func (*ResourceKind) UnmarshalJSON

func (k *ResourceKind) UnmarshalJSON(data []byte) error

UnmarshalJSON deserializes the canonical string form.

Parameters:

  • `data`: the JSON bytes; must be a string.

Returns:

  • `error`: non-nil on a non-string value or an unknown kind.

func (*ResourceKind) UnmarshalText

func (k *ResourceKind) UnmarshalText(text []byte) error

UnmarshalText deserializes the canonical string form — the seam op.Convert's text-unmarshal step and the defaults vocabulary use, so an authored `kind="regular"` (and the `kind=entry` default) land as typed values.

Parameters:

  • `text`: the canonical lowercase form.

Returns:

  • `error`: non-nil on an unknown kind.

func (*ResourceKind) UnmarshalYAML

func (k *ResourceKind) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML deserializes the canonical string scalar form.

Parameters:

  • `value`: the YAML node; must be a string scalar.

Returns:

  • `error`: non-nil on a non-scalar node or an unknown kind.
type SymbolicLink struct {
	// contains filtered or unexported fields
}

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) Addressing

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

Addressing reports that file.entry 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 (*SymbolicLink) BindRoot

func (r *SymbolicLink) BindRoot(root fsroot.Dir)

BindRoot re-binds this resource's location to `root`, rel-first — the activation binding (§5.5), driven from the executor's pre-flight resolve pass through the op.RootBinder seam.

Identity (the rel) is unchanged; the root becomes the run's; the native form derives. The environment re-base happens executor-side, so after binding every observation — existence, Etag, Digest, I/O — reads the run's world.

Parameters:

  • `root`: the run's bound fsroot.

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 [entry.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) ConvertTo

func (r *SymbolicLink) 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 (*SymbolicLink) Digest

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

Digest returns the honest content hash of the link itself: sha256 of its target in canonical slash form, 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 from readlink with no cleaning and no absolutization — only separator canonicalization to slash form, because Windows reads a created link back with native separators and equal logical targets must digest equally on every platform (the same rule fsroot.Path's Rel follows for document bytes; #556). 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 [entry.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) Exists

func (r *SymbolicLink) Exists() bool

Exists reports whether the symlink itself exists, without following it — a link's existence is the link, not its target (the claim-verification defect this fixes: a deployed link whose target sits outside the run's root verified through Stat, followed the link out of confinement, and was falsely marked Gone).

Returns:

  • `bool`: true when the path holds a symbolic link (lstat plus kind test — kind-honest activation, ruled 2026-08-22); false on any lstat error or any other kind.

func (*SymbolicLink) IsDir

func (r *SymbolicLink) 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 (*SymbolicLink) MismatchesKind

func (r *SymbolicLink) MismatchesKind() bool

MismatchesKind reports whether the path holds an entry that is not a symbolic link — the seam that separates a wrong-kind claim from an absent one (op.KindMismatcher).

Only a mismatch is intolerable: op.MissingResourcePolicyIgnore means "the goal already holds", which is true of absence and false of a surprise.

Returns:

  • `bool`: true when an entry is there and it is not a symbolic link.

func (*SymbolicLink) Path

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

Path returns the canonicalized absolute path handle on the disk.

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

Returns:

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

func (*SymbolicLink) Resolve

func (r *SymbolicLink) Resolve() error

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

Shadows [entry.Resolve], whose existence check goes through fsroot.Dir's Stat and therefore FOLLOWS the link — an escaping or absolute target would turn the check into the kernel's containment refusal even though the link itself landed exactly as asked (#556). The link is the resource, not its referent (ruling 5b), so the check here is lstat: a dangling or escaping target is a legal on-disk state, and any follow is judged by the kernel at use.

Returns:

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

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