remoteenv

package
v0.0.34 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package remoteenv is a deterministic, in-process, protocol-level reference fake of a REMOTE execution environment (ADR 0214, issue #462 phase 3). It is a CONTRACT PROOF only — it has NO network, NO external dependency, and NO global singleton. It proves the phase-3 persistence/reattachment/fork/merge contract against a non-in-tree EnvironmentKind without waiting for a real vendor transport, and it is deliberately NOT wired by default app.Build.

The fake is structured around a Backend that owns an ID→namespace registry. Each namespace is an isolated in-memory file map with content-hash version tokens (the same version discipline the in-tree memfs adapter uses, so the conditional-CAS / create-only semantics hold across multiple handles to the same namespace). NewEnvironment(id) / Resolve(ctx, ref) return a Workspace + CommandRunner bound to the SAME opaque namespace; EnvironmentForker returns a complete isolated child Environment with a fresh opaque child ID and cleanup; EnvironmentMerger applies child changes to the parent by REF, preserving the child on conflict.

The fake runner implements a deliberately tiny, documented TEST protocol (`cat <path>` and `write <path> <content>`); it does not hand-roll a general shell. The file API write/read and the fake Shell runner observe the SAME namespace in both directions, so a forked child's Shell observes the same namespace its Read/Write do.

The fake's EnvironmentKind label is `remote-fake` (a package-level const in THIS package, NEVER a constant in the public engine/session — the EnvironmentKind set is open). The zero session.EnvironmentRef and the in-tree Kinds (local/mem/nofs) never reach this package.

Index

Constants

View Source
const (
	Kind session.EnvironmentKind = "remote-fake"
)

Kind is the EnvironmentKind label this fake mints. It is a package-level constant in THIS adapter package, never a constant in the public engine/session — the EnvironmentKind set is open, and a real remote transport (or another out-of-tree backend) adds its own label without widening the session package.

Variables

View Source
var (
	// ErrNilSource is returned by RehydrateForTest when src is nil.
	ErrNilSource = errors.New("remoteenv: rehydrate source workspace is nil")
	// ErrNamespaceExists is returned by RehydrateForTest when id already names a
	// registered namespace (refuse-to-clobber).
	ErrNamespaceExists = errors.New("remoteenv: namespace id already exists (refuse to clobber)")
)

RehydrateForTest re-seeds the backend with a namespace under id, copying the current file state from src (a tool.Workspace from another Backend that minted the same id). It is the durable-stand-in for a remote backend's persistent state across a simulated restart: a real transport reattaches without copying (the state survives the process), but the in-memory fake simulates that by re-seeding so the Resolve path proves it reattaches to the same state. It is intended for the phase-3 reattachment contract proof only.

It FAILS HONESTLY rather than panicking/clobbering (issue #462 phase-3 finding #5): a nil src returns ErrNilSource, and an id that ALREADY names a registered namespace returns ErrNamespaceExists rather than silently overwriting it (a stale or accidental second rehydrate would otherwise mask the live namespace's state and hide a real bug). A test that intends to replace must explicitly delete first (the fake exposes no Delete; build a fresh Backend for a clean slate).

View Source
var ErrEmptyCommand = errors.New("remoteenv: empty command")

ErrEmptyCommand is returned by the runner for a blank command string.

View Source
var ErrUnknownNamespace = errors.New("remoteenv: unknown namespace id")

ErrUnknownNamespace is returned by Resolve/NewEnvironment when the requested namespace id does not exist in the Backend's registry. A non-in-tree ref MUST name a namespace the Backend created; the fake does not fabricate one on demand (honest failure, never a silent empty workspace).

Functions

This section is empty.

Types

type Backend

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

Backend owns an ID→namespace registry and is the single construction site for fake remote Environments. It is deterministic, in-process, and holds no network or external resource. The zero value is NOT usable; construct with NewBackend. It is safe for concurrent use.

func NewBackend

func NewBackend() *Backend

NewBackend returns an empty Backend.

func (*Backend) NewEnvironment

func (b *Backend) NewEnvironment(label string) (tool.Environment, error)

NewEnvironment creates a FRESH namespace under a new opaque id and returns a complete Environment (Workspace + CommandRunner bound to that namespace). It is the construction path: it does not resolve an existing ref (use Resolve for reattachment). The id is opaque and Backend-owned.

func (*Backend) RehydrateForTest

func (b *Backend) RehydrateForTest(id string, src tool.Workspace) error

RehydrateForTest re-seeds the backend with a namespace under id, copying the current file state from src. See the package-level comment above for the full contract (the phase-3 reattachment stand-in, the nil/clobber guards).

func (*Backend) Resolve

Resolve reattaches a LIVE Environment to the namespace named by ref.ID, proving the restart-reattachment contract: a restarted process with a persisted ref resolves it back to the SAME backend state. The ref's Kind MUST be Kind; a mismatch is an honest error. The returned Environment's Ref() equals the requested ref (the Backend never rewrites identity).

type Forker

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

Forker is the fake's tool.EnvironmentForker. Fork creates a complete isolated child Environment with a FRESH opaque child id, seeded from a DEEP COPY of the parent namespace's file map, so the child starts from the parent's contents and the two then diverge independently. The child's Workspace and runner share the child's namespace, so the child's Shell observes the SAME namespace its Read/Write do — never the parent's. cleanup is a no-op (the namespace is in-memory; a real transport would tear down the remote workspace here).

func NewForker

func NewForker(backend *Backend) *Forker

NewForker constructs the fake's EnvironmentForker over backend. The backend is the SAME one that minted the parent Environment, so the child namespace is registered alongside the parent and the two share no mutable state.

func (*Forker) Fork

func (f *Forker) Fork(_ context.Context, base tool.Environment, label string) (tool.Environment, func() error, string, error)

Fork creates an isolated child Environment derived from base. The child namespace is seeded from a deep copy of base's namespace file map. The child carries the SAME Kind with a fresh opaque id; the parent is never mutated.

type Merger

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

Merger is the fake's tool.EnvironmentMerger. Merge applies the child's working-tree-vs-parent diff to the parent namespace by REF, preserving the child on conflict (the contract: a failed merge is recoverable). It computes the child-only and child-modified paths against the parent and applies them; a path that exists in both with DIFFERENT content is a conflict — the parent keeps its content, the conflict path is reported, and the child is left intact.

func NewMerger

func NewMerger(backend *Backend) *Merger

NewMerger constructs the fake's EnvironmentMerger over backend.

func (*Merger) Merge

func (m *Merger) Merge(_ context.Context, child, parent tool.Environment) error

Merge applies the diff of the fork at child into the parent Environment's namespace. It reads both namespaces by ref, computes the changes (new + modified files in the child vs the parent), and applies them to the parent. On conflict (a path present in both with different content) it returns a non-nil error naming the conflicts and leaves the child intact.

Concurrent merges are serialized through a process-wide mutex so two merges into the same parent never interleave their read-compute-apply sequences (mirrors the forker's SerializingMerger).

Jump to

Keyboard shortcuts

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