mdriver

package module
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

README

m-driver-sdk — the shared engine-driver SDK for the m toolchain

m-driver-sdk (Go package mdriver) is the single coupling point between m-cli and the engine drivers. It encodes the vendor-neutral engine-driver contract v1.0 as Go types and the verb-level Transport interface every m-<engine> driver implements. m-cli speaks only the contract; all proprietary detail lives behind the Transport in each driver (m-ydb, m-iris).

It was extracted at the Phase-0 reconciliation checkpoint by freezing the two driver Transport sketches against one interface — see phase-0-reconciliation.md.

Orchestration

This repo is the owner and orchestrator of the m-engine-drivers implementation. Cross-repo status and the protocols that keep m-ydb + m-iris parallel and consistent live here:

What's in it

Symbol Purpose
Transport the frozen verb-level seam: Health · Load · Exec · ReadGlobal · SetGlobal
ExecRequest/ExecResult field-dispatched exec (Script > EntryRef > Command); EngineError carries §7 faults
LoadRequest/LoadResult, GlobalRef/GlobalNode, Health the request/result types
EngineError the §7 structured engine fault (mnemonic/routine/line/text)
Caps/Axes/Features, ContractVersion, Transport* consts the capability document (§4); Axes.Wired() iterates advertised axes in contract order
FakeTransport the function-field fake for driver unit tests (no engine)

Design rules

  • Verb-level, not run(argv) (risk B1). A low-level argv seam cannot model both YottaDB's session-pipe (stdin→stdout) and IRIS's Atelier-SQL remote (HTTP PUT + SQL; results via a result global). Each transport implements its own strategy; the rest of a driver is transport-agnostic.
  • Vendor-neutral only. No YottaDB/IRIS specifics here.
  • No clikit dependency. clikit (the toolchain envelope/styling layer) is vendored into every toolchain repo, including non-driver ones; a clikit→SDK edge would couple the whole toolchain to the driver SDK. EngineError is therefore defined here for transport results, and clikit keeps its own copy for the JSON envelope — drivers convert at the boundary.
  • Versioning. Adding verbs/fields is a back-compatible minor bump; changing or removing one is a major bump (contract §8). ContractVersion is the handshake m-cli negotiates on.

Consuming it (local development)

Until the module is published, drivers depend on it via a local replace:

require github.com/vista-forge/m-driver-sdk v0.0.0
replace github.com/vista-forge/m-driver-sdk => ../m-driver-sdk

Drop the replace and pin a tagged version once it is published.

Documentation

Overview

Package mdriver is the shared engine-driver SDK for the m toolchain: the vendor-neutral contract types (driver-contract.md v1.0) and the verb-level Transport seam that every m-<engine> driver implements and that m-cli depends on. It is the single coupling point between m-cli, m-ydb, and m-iris — m-cli speaks only the contract; all proprietary detail lives behind the Transport in each driver.

Extracted at the Phase-0 reconciliation checkpoint by freezing the two driver Transport sketches (m-ydb's session-pipe and m-iris's Atelier-SQL) against one interface. Adding verbs/fields is a back-compatible minor bump; changing or removing one is a major bump (driver-contract.md §8).

The package holds ONLY vendor-neutral types — no YottaDB or IRIS specifics. clikit (the toolchain envelope/styling layer) is intentionally NOT imported: clikit is vendored into every toolchain repo, including non-driver ones, so a clikit→SDK dependency would couple the whole toolchain to the driver SDK. EngineError is therefore defined here for Transport results; clikit keeps its own copy for the JSON envelope and drivers convert at the boundary.

Index

Constants

View Source
const (
	TransportLocal  = "local"
	TransportDocker = "docker"
	TransportRemote = "remote"
)

Transport selectors (driver-contract.md §3). A driver advertises which it supports via caps.transports; YottaDB supports local+docker, IRIS adds remote.

View Source
const (
	EnvRunLockToken = "M_RUN_LOCK_TOKEN"
	EnvRunLockKey   = "M_RUN_LOCK_KEY"
)

Env var names for the bracket. Scope: ephemeral, process-subtree, never committed. The token rides argv (--run-lock, the loud channel) or env (the fallback that carries a bash-wrapped chain); the explicit flag wins.

View Source
const (
	// CodeRunLockRequired — a bracket-requiring verb was called with no token.
	CodeRunLockRequired = "RUN_LOCK_REQUIRED"
	// CodeRunLockMismatch — a token was presented but does not match the live
	// holder's record. This is the fencing refusal: a dead bracket's orphan child
	// still presenting the old token after a new lane has acquired.
	CodeRunLockMismatch = "RUN_LOCK_MISMATCH"
	// CodeRunLockNotHeld — a token was presented but NO bracket is held: the
	// bracket died under its own children.
	CodeRunLockNotHeld = "RUN_LOCK_NOT_HELD"
	// CodeRunLockTimeout — a bounded acquire wait expired (acquire side).
	CodeRunLockTimeout = "RUN_LOCK_TIMEOUT"
	// CodeRunLockHolderStopped — the holder is SIGSTOPped (state T). The wait
	// stops EARLY and returns the mechanized triage instead of burning the
	// timeout. Never an auto-break: the 6 h suspended-writer incident is precisely
	// a lock that LOOKED dead for hours and was not (R-5).
	CodeRunLockHolderStopped = "RUN_LOCK_HOLDER_STOPPED"
	// CodeEngineBusy — the try-mode (Wait == 0) outcome on a held lock. NOT an
	// error condition: the caller proceeds in report-only mode and emits this as a
	// distinct machine-readable outcome naming the holder — never a bare green
	// (R-6; an unobserved gate is not a gate).
	CodeEngineBusy = "SKIPPED_ENGINE_BUSY"
	// CodeRunLockDirForbidden — a lock-home override (driver argv --run-lock-dir,
	// RunLockOptions.Dir, or Client.RunLockDir) was set in a NON-TEST binary. The
	// override decouples the bracket from the real serialization domain — a real
	// run that honors it locks a file no other lane looks at (DS-19) — so outside
	// a `go test` binary it is refused, never honored (hardening L0.B1, Dev-11).
	CodeRunLockDirForbidden = "RUN_LOCK_DIR_FORBIDDEN"
)

Refusal codes (§6e). They enter the declared error vocabulary (undeclared-seams D-1); a consumer branches on the CODE, never on prose.

View Source
const (
	HolderUnheld  = "UNHELD"
	HolderRunning = "RUNNING"
	HolderStopped = "STOPPED"
	// HolderOrphanFD — flock says held, but the recorded holder is dead. An
	// inherited FD in a surviving child (e.g. an orphaned `docker exec` client)
	// keeps the lock held after the acquirer died. Named so this is visible rather
	// than mysterious; it is still NOT auto-broken.
	HolderOrphanFD = "HELD_BY_ORPHAN_FD"
)

Holder states reported by runlock-status (§6d).

View Source
const CodeRunLockUnresolved = "RUN_LOCK_UNRESOLVED"

CodeRunLockUnresolved — the route's canonical locus could not be determined, so no key exists and a bracket-requiring verb on it is REFUSED (fail-closed, §R-9.3).

This must NEVER degrade into a selector-spelled key: that would reintroduce the two-routes-one-instance split precisely when the box is degraded (defeat (n)).

View Source
const ContractVersion = "1.1"

ContractVersion is the driver-contract major.minor this SDK encodes. A driver advertises it in caps.contract; m-cli refuses a driver whose major it does not understand, with an upgrade hint (driver-contract.md §8). 1.1 (2026-07-16): additive — Caps gains the optional `limits` block (§4a), the measured per-connection engine/lane limits from the engine-capability fact-check (docs repo proposals/engine-capability-registry/, owner-accepted §4).

View Source
const ExitRunLockRefused = 4

ExitRunLockRefused is the contract exit code every run-lock refusal carries: the §2 ladder's EXISTING refusal rung (4 — clikit's ExitRefused, whose own definition already reads "conflict / refusal (lock held, …)"). Distinctness lives in the CODE, which is the machine-branching key; minting a new integer would fall off the contract's exit ladder, force a change in every vendored clikit copy, and be missed by m-cli's existing exit-4 handling.

Variables

View Source
var DefaultRunLockDirName = filepath.Join("data", "vista-forge", "run-lock")

DefaultRunLockDirName is the run-lock home under the user's home directory (R-4): ~/data/vista-forge/run-lock.

Deliberately NOT /tmp. The sharp reason is not reboots: a tmp-cleaner (systemd-tmpfiles) can unlink a lock file WHILE IT IS HELD. flock survives the unlink (the FD holds the inode), but the next acquirer open(O_CREATE)s a NEW inode at the same path and locks it successfully — two lanes each holding "the" lock on different inodes, mutual exclusion silently broken by the filesystem. The lock home must be a directory nothing reaps.

View Source
var ErrNoDocker = errors.New("docker CLI not found")

ErrNoDocker — docker is not installed. For a NETWORK route this is a real answer, not a failure: a box with no docker runs no containers, so the engine is foreign.

View Source
var ErrNoSuchContainer = errors.New("no such container")

ErrNoSuchContainer is the clean-no-match sentinel. **Exported** (v0.8.1) so a custom DockerRunner can express it: in v0.8.0 it was unexported and minted only inside the real closure, so an injected runner could produce nothing but an infrastructure error — and the self-canonical rung, the one that dissolves the bootstrap problem, was untestable by any consumer.

Functions

func ClassifiedVerbs added in v0.7.0

func ClassifiedVerbs() map[string]VerbClass

ClassifiedVerbs returns the registry as a copy, for rendering the contract's class table and for iteration. It is a copy on purpose: an exported mutable map would let a consumer reclassify `exec eval` as `status` at runtime and exempt itself from the very enforcement it is calling through (defeat (d)).

func DefaultRunLockDir added in v0.7.0

func DefaultRunLockDir() (string, error)

DefaultRunLockDir is the run-lock home (R-4). See DefaultRunLockDirName for why it is not /tmp.

It derives from passwd (user.Current().HomeDir), NEVER the $HOME env: $HOME is an unmeasured ambient input, and two processes with divergent $HOMEs would derive two lock files for one engine — two lanes each "holding" the bracket while mutating the same instance (the measured D9/EXP-1 defeat). passwd is byte-identical however the environment is dressed (DS-1). On a passwd-lookup failure it fails CLOSED: no fallback, because falling back to $HOME would reopen D9 on exactly the hosts where passwd is broken.

func ExecRunner

func ExecRunner(ctx context.Context, name string, args []string) (stdout, stderr []byte, exit int, err error)

ExecRunner is the production CmdRunner: it runs the driver binary, capturing stdout and stderr separately. A non-zero exit is a result; only a launch failure is an error.

func HashSelector added in v0.7.0

func HashSelector(s string) string

HashSelector is the selector for a target identified by a filesystem interior (m-ydb local: the realpath of the derived global directory) — a short digest, so the interior never appears in a lock-file name.

func IsEngineBusy added in v0.7.0

func IsEngineBusy(err error) bool

IsEngineBusy reports the try-mode outcome: the engine is held by another lane, so a non-waiting caller (G-LEAK-SCAN's cron) must record SKIPPED_ENGINE_BUSY naming the holder — never a bare green, and never a red.

func Locate

func Locate(engine string, d LocateDeps) (string, error)

Locate finds the m-<engine> driver binary, in the contract's resolution order (driver-contract.md §4): $M_<ENGINE>_BIN → next to the running executable → the sibling source checkout's dist/ (…/m-<engine>/dist/m-<engine>) → $PATH.

func RequireRunLock added in v0.7.0

func RequireRunLock(class VerbClass, ref RunLockRef, token string) error

RequireRunLock is the enforcement decision, and it lives here so that both drivers make it identically: a class that needs a bracket must prove one; a status or control verb runs unconditionally.

A driver's dispatch middleware is exactly: look the verb up in the registry (ClassOf), call this, and render a refusal onto the envelope. The middleware is the choke point — a hurried developer can evade a convention, but not the dispatch path they call through.

func UnclassifiedVerbs added in v0.7.0

func UnclassifiedVerbs(c Caps) []string

UnclassifiedVerbs returns the "<axis> <verb>" entries a driver advertises in caps that carry no class — sorted, so the conformance failure is stable. A non-empty result is a RED: the driver has a verb its own dispatch middleware cannot enforce, which is how an unenforced verb would otherwise be added.

func UnresolvedRunLock added in v0.8.0

func UnresolvedRunLock(engine string, cause error) error

UnresolvedRunLock builds the fail-closed refusal, carrying WHY resolution failed.

func VerifyRunLock added in v0.7.0

func VerifyRunLock(ref RunLockRef, token string) error

VerifyRunLock is the ownership check a driver runs at call entry (§R-2). It is one non-blocking syscall plus one small read — negligible against a docker exec or Atelier round trip — and it NEVER blocks: the driver never waits on the lock, so a driver call cannot deadlock on it. Waiting happens only in the orchestrator's acquire.

Types

type Axes

type Axes struct {
	Lifecycle []string `json:"lifecycle,omitempty"`
	Sync      []string `json:"sync,omitempty"`
	Exec      []string `json:"exec,omitempty"`
	Data      []string `json:"data,omitempty"`
	Cover     []string `json:"cover,omitempty"`
	Admin     []string `json:"admin,omitempty"`
	Meta      []string `json:"meta,omitempty"`
}

Axes lists the advertised verbs per contract axis (driver-contract.md §5). It is a struct (not a map) so the JSON field order is the contract's logical order; each axis is omitempty so a driver advertising only its wired axes leaves the rest nil → absent (the honest-incremental model both drivers use).

func (Axes) Wired

func (a Axes) Wired() []AxisVerbs

Wired returns the non-empty axes in the contract's logical order, so callers (caps text rendering, conformance) can iterate advertised axes without re-listing field names. Empty (unwired) axes are skipped.

type AxisVerbs

type AxisVerbs struct {
	Name  string
	Verbs []string
}

AxisVerbs pairs an axis name with its advertised verbs (for iteration).

type Caps

type Caps struct {
	Engine   string `json:"engine"`
	Contract string `json:"contract"`
	// Transport is the connection this document describes — the resolved transport,
	// not a list. Additive in v0.8.0; conformance (C-CAPS-1) asserts it matches the
	// transport under test, which is what makes every other field in the document
	// honest about the connection the caller actually has.
	Transport  string   `json:"transport,omitempty"`
	Transports []string `json:"transports"`
	Axes       Axes     `json:"axes"`
	Features   Features `json:"features"`
	// Limits is the measured capability block (driver-contract.md §4a, contract
	// 1.1): what THIS engine over THIS connection actually carries, so consumers
	// ask instead of hardcoding one engine's number into both (the STDCOMPRESS
	// defect shape). nil = the driver has not taken its limits slice — consumers
	// keep their gated constants; a PRESENT block must be honest and conformance
	// (C-CAPS-2) checks its shape and will grow live spot-measurement.
	Limits *Limits `json:"limits,omitempty"`
}

Caps is the capability document emitted by `meta caps` (driver-contract.md §4). m-cli probes it before optional verbs and adapts to exactly what is advertised; calling an unadvertised verb yields exit 7. Caps MUST be honest — advertise only axes/verbs actually wired in the build — and conformance enforces it. Caps DESCRIBES THE CONNECTION, not the binary (design §R-9.7, owner decision #7). A driver builds it from the resolved connection — `CapsDoc(conn)` — because the binary's union of capabilities is a lie to every caller on a specific transport: m-iris advertised `exec script` on all three transports while the remote transport refused it at runtime, so a remote caller negotiated "supported" and then failed.

This matters most for Features.RunLock: owner decision #4 (an old driver is a HARD ERROR, never a silent unlocked run) is built on trusting that bit, and a per-binary bit would lie exactly the same way a per-binary `script` did.

type Check

type Check struct {
	Name   string `json:"name"`
	OK     bool   `json:"ok"`
	Detail string `json:"detail,omitempty"`
	Fix    string `json:"fix,omitempty"`
}

Check is one doctor preflight result (driver-contract.md §5.7, plan §3): a typed, named diagnostic with an optional human detail and a fix hint.

type Client

type Client struct {
	Bin       string   // path to the m-<engine> binary
	Engine    string   // "ydb" | "iris" (for error messages)
	Transport string   // local | docker | remote
	ConnArgs  []string // extra connection flags (e.g. --container, --base-url); usually empty (driver reads M_<ENGINE>_* env)

	// RunLockToken proves this client's calls belong to a live run bracket. When
	// set, every driver invocation carries `--run-lock <token>` — the LOUD channel,
	// which wins over the M_RUN_LOCK_TOKEN env fallback the driver reads for a
	// bash-wrapped chain. Set it with RunLock.Bind, never by hand.
	//
	// Unset means "no bracket proven by this client": a mutating or read verb will
	// be REFUSED by the driver's dispatch unless the ambient env carries a valid
	// token. That refusal is the point — the corruption class becomes an
	// error-message class (run-lock design §8a).
	RunLockToken string
	// RunLockDir overrides the run-lock home on every invocation, so the driver's
	// dispatch check reads the SAME lock file the bracket holds. Tests and fixtures
	// only.
	RunLockDir string
	// contains filtered or unexported fields
}

Client is the SDK's reference engine client: the single, shared way any tool reaches a live engine over the driver contract. It invokes an m-<engine> driver binary over the contract's subprocess + JSON-envelope seam (driver-contract.md §2) — `m-<engine> <axis> <verb> [args] --transport <t> [conn] --output json` — and parses the one JSON envelope it writes. A caller speaks only the neutral contract (§1, §11: "zero changes to add an engine"); all vendor detail lives behind the binary, which the drivers reach themselves (m-ydb via local/docker/SSH, m-iris via Atelier REST), so this client never knows the wire, only the contract.

It is the seam's transport monopoly: m-cli and every `v` tool import this Client rather than hand-rolling transport or vendoring a copy (~/vista-forge/CLAUDE.md, waterline rule 3). One Client drives one m-<engine> binary over one transport+connection.

func NewClient

func NewClient(bin, engine, transport string, connArgs []string, run CmdRunner) *Client

NewClient builds a driver client. A nil run uses the real subprocess runner.

func (*Client) Caps

func (c *Client) Caps(ctx context.Context) (Caps, error)

Caps fetches the driver's capability document.

func (*Client) ExecEval

func (c *Client) ExecEval(ctx context.Context, command string) (ExecResult, error)

ExecEval evaluates a single M command. A §7 engine fault is returned in ExecResult.EngineError (data), not as a Go error — only a transport/launch failure is a Go error.

func (*Client) ExecRun

func (c *Client) ExecRun(ctx context.Context, entryref string, args []string) (ExecResult, error)

ExecRun runs an entryref (args become $ZCMDLINE / the formallist).

func (*Client) ExecScript added in v0.4.0

func (c *Client) ExecScript(ctx context.Context, script string) (ExecResult, error)

ExecScript runs a multi-line M/ObjectScript script (ExecRequest.Script) and returns its raw captured device output — the transport feeds it to the engine's direct mode (YDB `-direct`) or a raw `iris session` pipe, appending the terminating halt. Unlike ExecEval/ExecRun there is no $ETRAP/TRY-CATCH wrapper: a script is a self-contained compound operation (e.g. the coverage TRACE / LineByLine passes) whose caller parses the raw dump itself, so faults surface only as absent/garbled output, never as a structured engineError. The script is base64-passed on argv so newlines and quotes cross the subprocess seam unmangled without widening the CmdRunner signature with a stdin channel.

func (*Client) Load

func (c *Client) Load(ctx context.Context, paths []string) (LoadResult, error)

Load stages + compiles routine source (exec load).

func (*Client) LoadNoCompile added in v0.4.0

func (c *Client) LoadNoCompile(ctx context.Context, paths []string) (LoadResult, error)

LoadNoCompile stages routine source WITHOUT the eager ZLINK/OBJ.Load compile (exec load --no-compile). This is the lazy-staging model a test/coverage run needs: drop every candidate routine on the engine's source path and let it auto-compile only what a suite actually references — so an optional module that fails to compile on this engine (e.g. a YDB-only deviceparam in STDNET) does not fail the whole stage when no running suite references it.

func (*Client) RunLockPath added in v0.7.0

func (c *Client) RunLockPath(ctx context.Context, dir string) (RunLockRef, error)

RunLockPath runs `meta runlock-path` — the driver's single key/path derivation authority (run-lock design R-3). dir is the tests-only home override.

It is a CONTROL-class verb, so it takes no token: that is what lets AcquireRunLock learn where the lock lives BEFORE a bracket exists. A token-gated derivation verb could never be reached, and the bracket could never be taken at all (§7.3).

func (*Client) RunLockStatus added in v0.7.0

func (c *Client) RunLockStatus(ctx context.Context) (RunLockStatus, error)

RunLockStatus runs `meta runlock-status` — the driver's measurement of the bracket: the holder record, the /proc/locks holder PIDs, and the holder's process state (UNHELD | RUNNING | STOPPED | HELD_BY_ORPHAN_FD). Also control class: a stuck run must always be observable.

func (*Client) StageSweep added in v0.5.0

func (c *Client) StageSweep(ctx context.Context, ttlHours int) (SweepResult, error)

StageSweep removes stale run-staging directories (`mtest-*`, older than ttlHours) under the engine's primary routine source dir (exec sweep) and returns their names. It is the self-healing half of the managed staging namespace (Features.ManagedStaging): a run that leaked its staging dir — crash, kill, lost host — is retired by the next run's sweep instead of shadowing installed routines forever. Only directories matching the staging prefix are touched, never flat files (a flat .m in the patch dir may be a genuine local patch).

func (*Client) Status

func (c *Client) Status(ctx context.Context) (Status, error)

Status runs `lifecycle status` — the reachability + identity probe (running, healthy, version). This is the engine-neutral way to prove a VistA is live and learn its version banner (the portable replacement for `W $ZV`, which only captures device output on YottaDB).

func (*Client) SupportsRunLock added in v0.7.0

func (c *Client) SupportsRunLock(ctx context.Context) (bool, error)

SupportsRunLock reports whether the driver advertises Features.RunLock — i.e. whether it actually enforces the bracket in its dispatch.

This is how a consumer asks the question owner decision #4 turns on: a caller must NEVER run unlocked while believing it is protected. An old driver (which omits the field, so it reads false) is a hard error with an upgrade hint in m-cli, not a silent unlocked run — otherwise "a net that trusts a self-report" simply re-enters at the version seam.

func (*Client) Unstage added in v0.5.0

func (c *Client) Unstage(ctx context.Context) (UnstageResult, error)

Unstage removes the run's own staging directory — the --stage-dir the connection was built with (exec unstage). Callers defer it unconditionally (abort paths included) so a completed run leaves no staging debris.

type CmdRunner

type CmdRunner func(ctx context.Context, name string, args []string) (stdout, stderr []byte, exit int, err error)

CmdRunner runs the driver binary and returns its stdout, stderr, and process exit. Only a launch failure (binary missing, etc.) is a Go error; a non-zero engine/driver exit is a result. Injected so the client is testable without a real driver binary.

type CoverResult

type CoverResult struct {
	LCOV         string  `json:"lcov"`
	CoveredLines int     `json:"coveredLines"`
	TotalLines   int     `json:"totalLines"`
	LinePercent  float64 `json:"linePercent"`
}

CoverResult is the `cover trace` payload (driver-contract.md §5.5). The driver runs the entryref under the engine's line tracer, reconciles the raw per-line hit data against the executable-line set, and emits an LCOV tracefile plus the rolled-up line totals. Both engines converge on executable-line granularity:

  • m-ydb: view "TRACE":1:"^ycov" … zwrite ^ycov (label-relative offsets, reconciled to absolute lines via the parse tree);
  • m-iris: %Monitor.System.LineByLine → MLINE:<routine>:<line>:<count> (already absolute); remote rides the runner class.

The neutral shape is intentionally line-coverage only (LCOV DA/LF/LH) — neither engine natively emits function or branch coverage. m-cli aggregates LCOV across drivers and applies --min-percent; the counts here let it gate without re-parsing.

All fields always render: a zero is meaningful (0% covered is a fact, not absence), the same convention as Status.LatencyMs and the Features flags.

type DockerError added in v0.8.1

type DockerError struct {
	Args   []string
	Exit   int
	Stderr string
}

DockerError is a non-zero docker exit. Its Stderr is kept because the exit code ALONE cannot say whether the container is missing or the daemon is dead — see Canonicalize.

func (*DockerError) Error added in v0.8.1

func (e *DockerError) Error() string

type DockerLocator added in v0.8.0

type DockerLocator struct{ Run DockerRunner }

DockerLocator resolves docker loci. A nil Run uses the real `docker` binary.

func NewDockerLocator added in v0.8.0

func NewDockerLocator() *DockerLocator

NewDockerLocator binds to the real docker CLI.

func (*DockerLocator) Canonicalize added in v0.8.0

func (d *DockerLocator) Canonicalize(ctx context.Context, selector string) (Locus, error)

Canonicalize turns a docker selector — a name OR an id, a running OR a stopped container — into the container's canonical NAME. This is what collapses the `--docker <id>` / `--docker <name>` alias onto one key (defeat (i)).

A container that does NOT exist is not an error: `lifecycle provision --docker foo` is about to CREATE it, and `foo` is exactly its canonical locus. That is what makes docker routes self-canonical and lets the most destructive verbs key without an engine call.

A DAEMON failure IS an error, and the caller must refuse (RUN_LOCK_UNRESOLVED) — never fall through to the selector spelling.

func (*DockerLocator) MatchNode added in v0.8.0

func (d *DockerLocator) MatchNode(ctx context.Context, node string) (Locus, bool, error)

MatchNode maps an engine-reported node fingerprint (its hostname, which for a container defaults to its short ID) onto a local container.

This is the line that closes R-9: a remote/Atelier route whose engine turns out to be a container on this host derives THAT CONTAINER'S docker locus — so `m test --docker foia-t12` and `v pkg install --transport remote` land on one lock.

The three outcomes are contract (C-RL-8):

(locus, true,  nil) — the engine IS this local container
(zero,  false, nil) — a CLEAN no-match: the engine is genuinely foreign
(zero,  false, err) — an ENUMERATION ERROR, or an ambiguous match: REFUSE

The error-vs-no-match distinction is the whole point: collapsing them would either key a foreign engine as local (wrong) or refuse every foreign engine (useless).

type DockerRunner added in v0.8.0

type DockerRunner func(ctx context.Context, args ...string) ([]byte, error)

DockerRunner runs the docker CLI and returns its stdout. Injected so the locus helpers are unit-testable without a daemon.

A custom runner signals the two failure classes the SDK must tell apart:

ErrNoSuchContainer — a CLEAN no-match (the container does not exist)
ErrNoDocker        — docker is not installed on this box
anything else      — an infrastructure failure: REFUSE

type DoctorResult

type DoctorResult struct {
	Transport string  `json:"transport"`
	OK        bool    `json:"ok"`
	Checks    []Check `json:"checks"`
}

DoctorResult is the `meta doctor` payload: the resolved transport, an overall ok (all checks green), and the typed checks. Exit code is carried by the envelope (0 all-green / 6 unreachable / 5 a check failed), not duplicated here.

type DriverError added in v0.6.0

type DriverError struct {
	Engine  string // "ydb" | "iris"
	Command string // the envelope's command (e.g. "exec load <paths>")
	Code    string // error.code (e.g. RUNTIME, NO_NAMESPACE)
	Exit    int    // error.exit / envelope exit
	Message string // human-readable message
	Hint    string // optional remediation hint
}

DriverError is a driver- or transport-level failure surfaced from a Fail-path envelope (ok:false with an `error`, and NO engineError). Before this, `call` ignored `ok`, so such a failure yielded a zero-value result and a NIL Go error — the silent-0/0 hole (F5) where a failed stage/exec reported an empty "0/0, ok:false" suite with no diagnostic. Callers can branch on Code / Exit, or just propagate it. A §7 ENGINE fault (ok:false WITH engineError) is NOT a DriverError — it stays data in the result's EngineError, per the contract.

func (*DriverError) Error added in v0.6.0

func (e *DriverError) Error() string

type EngineError

type EngineError struct {
	Routine  string `json:"routine,omitempty"`
	Line     int    `json:"line,omitempty"`
	Mnemonic string `json:"mnemonic,omitempty"`
	Text     string `json:"text,omitempty"`
}

EngineError is the structured engine fault (driver-contract.md §7). On any compile/runtime fault, exec/cover verbs set ok=false AND populate this so a RED suite shows the real cause (e.g. a <NOROUTINE> at a line) instead of passed:0, failed:0. Mnemonic carries the engine code: %YDB-E-…/%GTM-E… for YottaDB (from $ZSTATUS), or the IRIS <…> code (from $ZERROR).

A driver builds this from the transport result and maps it onto the clikit envelope's own EngineError field when rendering a failure.

type ExecRequest

type ExecRequest struct {
	Script   string
	EntryRef string
	Args     []string
	Command  string
	Stdin    string // optional principal-device input
	Prefix   string // ephemeral-run prefix (zzt<runid>)
}

ExecRequest runs one of three shapes, selected by which field is set rather than an explicit mode enum (the union of both drivers' needs). Precedence when more than one is set: Script, then EntryRef, then Command.

  • Script → a multi-line M/ObjectScript script (YDB `-direct`; the transport appends the terminating halt). Engines without a direct mode ignore it.
  • EntryRef→ run an entryref (`yottadb -run <ref>` / IRIS `do <ref>`); Args become $ZCMDLINE / the entry's formallist.
  • Command → evaluate a single M command (YDB `%XCMD` / IRIS `xecute`).

type ExecResult

type ExecResult struct {
	Stdout      string       `json:"stdout"`
	Status      int          `json:"status"`
	EngineError *EngineError `json:"engineError,omitempty"`
}

ExecResult is the unified outcome. Stdout is the captured device output (session transports) or the runner's result-global text (IRIS remote). EngineError, when non-nil, is the §7 structured fault — set instead of a Go error so the caller can render a RED-with-cause envelope.

type FakeCall

type FakeCall struct {
	Verb string
	Req  any
}

FakeCall is one recorded interaction. Req holds the request struct (or, for SetGlobal, a [2]string{ref, value}).

type FakeTransport

type FakeTransport struct {
	HealthFn     func(ctx context.Context) (Health, error)
	LoadFn       func(ctx context.Context, req LoadRequest) (LoadResult, error)
	ExecFn       func(ctx context.Context, req ExecRequest) (ExecResult, error)
	ReadGlobalFn func(ctx context.Context, req GlobalRef) (GlobalNode, error)
	SetGlobalFn  func(ctx context.Context, ref, value string) error

	Calls []FakeCall
}

FakeTransport is the injected Transport for driver unit tests (no engine). It records every call and returns canned results, so a command's behavior — envelope shape, engineError mapping, verb sequencing — is asserted without a real engine. Real transports appear only in the gated integration tier (plan §1, "No hidden engine in unit tests").

Set the *Fn fields to script behavior; an unset verb returns a zero result. Calls records an ordered trace for sequencing/argument assertions.

func (*FakeTransport) Exec

func (*FakeTransport) Health

func (f *FakeTransport) Health(ctx context.Context) (Health, error)

func (*FakeTransport) Load

func (*FakeTransport) ReadGlobal

func (f *FakeTransport) ReadGlobal(ctx context.Context, req GlobalRef) (GlobalNode, error)

func (*FakeTransport) SetGlobal

func (f *FakeTransport) SetGlobal(ctx context.Context, ref, value string) error

type Features

type Features struct {
	Remote          bool `json:"remote"`
	Prune           bool `json:"prune"`
	EphemeralPrefix bool `json:"ephemeralPrefix"`
	Snapshot        bool `json:"snapshot"`
	// ManagedStaging: exec load stages into a run-scoped subdirectory of the
	// primary source dir (--stage-dir), with exec unstage (exit cleanup) and
	// exec sweep (TTL retirement of leaked mtest-* dirs) — the self-healing
	// staging namespace. File-based engines (YDB) advertise it; an engine that
	// stages into a namespace (IRIS) has nothing to sweep and leaves it false.
	ManagedStaging bool `json:"managedStaging"`
	// RunLock: the driver ENFORCES the per-engine run-lock in its dispatch — it
	// derives the lock key (meta runlock-path), measures the holder (meta
	// runlock-status), and REFUSES any mutating/read verb that cannot prove a live
	// bracket (driver-contract.md §5.8).
	//
	// This bit is the driver's own promise, and conformance holds it to it: a
	// driver that leaves it false SKIPS the run-lock cases (visibly — a skip is
	// never a pass); a driver that advertises it must pass all six. An old driver
	// omits the field entirely, which unmarshals to false — so consumers can tell
	// "does not enforce" from "enforces", and hard-error rather than run unlocked
	// while believing themselves protected (owner decision #4).
	RunLock bool `json:"runLock"`
}

Features advertises optional capabilities m-cli negotiates for graceful degradation (driver-contract.md §4, §10). All fields are always rendered (no omitempty): a false flag is meaningful information, not absence.

type GlobalNode

type GlobalNode struct {
	Ref   string       `json:"ref"`
	Value string       `json:"value,omitempty"`
	Nodes []GlobalNode `json:"nodes,omitempty"`
}

GlobalNode is a global value, with children for a subtree read.

type GlobalRef

type GlobalRef struct {
	Ref   string
	Order string // "forward" | "reverse"
	Depth int    // 0 = this node only
}

GlobalRef addresses a global for a read. Ref is the full global reference (leading ^ optional), e.g. `^ycov("RTN",12)`. Order/Depth shape a subtree query (data.query); the zero value (empty Order, Depth 0) is a single-node get.

type Health

type Health struct {
	Running   bool   `json:"running"`
	Healthy   bool   `json:"healthy"`
	Version   string `json:"version,omitempty"`
	LatencyMs int64  `json:"latencyMs"`
}

Health is the probe result (driver-contract.md §3 health probes).

type Limits added in v0.10.0

type Limits struct {
	// MaxString is the engine's maximum local string length.
	MaxString int `json:"maxString"`
	// MaxNodeValue is the largest value one global node carries on this
	// instance. MaxNodeValueKind says whether that is physics or site config:
	//   "engine"        — the engine's own ceiling (IRIS long strings)
	//   "region-config" — a per-region/site setting (YDB max_rec_size): a
	//                     different site may carry more or less; re-derive there.
	MaxNodeValue     int    `json:"maxNodeValue"`
	MaxNodeValueKind string `json:"maxNodeValueKind"`
	// MaxKeyBytes bounds the total key (global name + subscripts, engine
	// counting); MaxSubscripts bounds subscripts per reference.
	MaxKeyBytes   int `json:"maxKeyBytes"`
	MaxSubscripts int `json:"maxSubscripts"`
	// EvalEnvelope is the largest command literal exec eval carries intact on
	// THIS lane; LoadBodyBytes the largest routine body exec load stages intact
	// (0 = no cliff found at package scale — measured, not unknown).
	EvalEnvelope  int `json:"evalEnvelope"`
	LoadBodyBytes int `json:"loadBodyBytes"`
	// FaultMnemonics maps the contract's oversize conditions to this engine's
	// fault mnemonic, so a consumer can translate a fault without an
	// engine-shaped regex. Keys (contract §4a): "oversize-string",
	// "oversize-node", "oversize-key", "too-many-subscripts".
	FaultMnemonics map[string]string `json:"faultMnemonics,omitempty"`
}

Limits carries the measured engine/lane limits for the resolved connection (driver-contract.md §4a). Values are FACTS with provenance, not tunables: each driver's numbers come from the engine-capability measured report (docs repo proposals/engine-capability-registry/engine-capability-registry-measured-report.md, 2026-07-16) or are derived live from the engine. Byte counts throughout.

The three bindings from the campaign:

  • a declared cap is a self-report — conformance re-measures (C-CAPS-2);
  • EvalEnvelope/LoadBodyBytes are per-TRANSPORT-LANE facts (the lane, not the engine, owns them — measured P3a/P3b);
  • the failure-mode map matters as much as the numbers (explainStageFault's single-engine regex was exactly the gap FaultMnemonics closes).

type LoadRequest

type LoadRequest struct {
	Paths  []string
	Dir    string
	Prefix string
}

LoadRequest stages source for exec (contract input `<paths…> | <dir>`). Paths are explicit files; Dir is an alternative directory of source. Prefix (e.g. zzt<runid>) namespaces an ephemeral run so teardown is scoped to that prefix.

type LoadResult

type LoadResult struct {
	Loaded      []string     `json:"loaded"`
	EngineError *EngineError `json:"engineError,omitempty"`
}

LoadResult reports what was staged + compiled; EngineError carries a compile fault (§7) when staging compiled routines fails.

type LocateDeps

type LocateDeps struct {
	Getenv   func(string) string          // environment lookup
	LookPath func(string) (string, error) // PATH resolution
	ExeDir   func() (string, error)       // directory of the running executable
	IsFile   func(string) bool            // path exists and is an executable file
}

LocateDeps are the injectable lookups used to find a driver binary (so the resolution order is unit-testable without a real filesystem/PATH).

func DefaultLocateDeps

func DefaultLocateDeps() LocateDeps

DefaultLocateDeps binds Locate to the real environment.

type Locus added in v0.8.0

type Locus struct {
	Class LocusClass
	ID    string
}

Locus is where an engine instance IS.

func (Locus) Key added in v0.8.0

func (l Locus) Key(engine string) string

Key renders the run-lock key. Grammar is unchanged from R-3 (`<engine>.<class>.<id>`, same normalization) — what changed is what the parts MEAN.

func (Locus) Valid added in v0.8.0

func (l Locus) Valid() bool

Valid reports whether the locus is resolved. The zero Locus is deliberately invalid: an unresolved route must refuse, never key.

type LocusClass added in v0.8.0

type LocusClass string

LocusClass is the class of an instance's canonical locus. It is NOT the dialed transport: a remote/Atelier route to a local container has class `docker`, because that is where the instance IS.

const (
	// LocusDocker — the instance is a container on this host. ID is its canonical name.
	LocusDocker LocusClass = "docker"
	// LocusLocal — the instance is a native install on this host. ID is the derived,
	// hashed interior (engine-instance-path ADR: derive, never declare).
	LocusLocal LocusClass = "local"
	// LocusRemote — the instance is FOREIGN: not a container here, not this host. ID is
	// built from the engine's OWN reported fingerprint, never the dialed spelling — which
	// is what unifies tunnels, DNS aliases and differing URL spellings onto one key.
	//
	// A foreign key carries the design's scope boundary in its own class name: flock is
	// per-kernel, so a foreign instance is serialized only among lanes on THIS driving
	// host. Lanes on other hosts are not covered (§8f, §R-9.8).
	LocusRemote LocusClass = "remote"
)

type RunLock added in v0.7.0

type RunLock struct {
	Key   string
	Path  string
	Token string
	// contains filtered or unexported fields
}

RunLock is a held (or joined) per-engine run bracket.

func AcquireRunLock added in v0.7.0

func AcquireRunLock(ctx context.Context, o RunLockOptions) (*RunLock, error)

AcquireRunLock opens a run bracket on the engine the Client points at.

It is the ONLY blocking wait in the design, and it holds nothing while it waits (acquire is the first act of a bracket) — which is why a maximal timeout parks one lane without transitively blocking anything (§7.4).

func JoinOrAcquireRunLock added in v0.7.0

func JoinOrAcquireRunLock(ctx context.Context, o RunLockOptions) (*RunLock, error)

JoinOrAcquireRunLock is the sanctioned consumer's auto-bracket: join the ambient bracket when there is a valid one for this target, else acquire one for the span of the command. It lives here, not in each consumer, so m-cli and v-pkg cannot drift on the reentrancy rule — the one rule that keeps the nested case from self-deadlocking.

func JoinRunLock added in v0.7.0

func JoinRunLock(ctx context.Context, o RunLockOptions) (*RunLock, error)

JoinRunLock verifies the AMBIENT bracket (M_RUN_LOCK_TOKEN, inherited from the process subtree) against the live record and returns a NON-OWNING handle.

This is the nested case (m test → v pkg install → driver call, defeat (d)): the inner layer verifies, it does not lock. No second acquire means no self-wait.

func (*RunLock) Bind added in v0.7.0

func (l *RunLock) Bind(c *Client)

Bind wires this bracket into a Client, so every subsequent driver invocation carries --run-lock (and --run-lock-dir when overridden) and the driver's dispatch check sees the same bracket this handle holds. One call, so a consumer cannot half-wire itself into a refusal.

func (*RunLock) Env added in v0.7.0

func (l *RunLock) Env() []string

Env returns the bracket's environment for child processes — the token rides the process subtree, so a nested sanctioned tool can JoinRunLock instead of deadlocking on a second acquire (defeat (d)).

func (*RunLock) Owned added in v0.7.0

func (l *RunLock) Owned() bool

Owned reports whether this handle ACQUIRED the bracket. Only the acquiring process releases; a joined handle's Release is a no-op (§R-2's reentrancy rule).

func (*RunLock) Release added in v0.7.0

func (l *RunLock) Release() error

Release ends the bracket. It is idempotent, and a NO-OP on a joined handle — only the acquirer releases. A joined Release that actually released would hand the engine to another lane in the middle of the outer run.

Callers must run cleanup (unstage, close sessions) BEFORE Release: state is restored before exclusivity is surrendered (§R-7). Note that release does NOT imply clean state — the lock asserts exclusivity, the census asserts state; keep the two claims separate.

type RunLockError added in v0.7.0

type RunLockError struct {
	Code   string
	Exit   int
	Key    string
	Path   string
	Msg    string
	Hint   string
	Status *RunLockStatus // the measured holder, when there is one
}

RunLockError is every run-lock refusal and acquire failure. Callers branch on Code; drivers map it onto the envelope's error object with Exit.

func (*RunLockError) Error added in v0.7.0

func (e *RunLockError) Error() string

type RunLockHolder added in v0.7.0

type RunLockHolder struct {
	PID   int    `json:"pid"`
	State string `json:"state,omitempty"` // /proc state letter: R, S, D, T (stopped), Z
	Cmd   string `json:"cmd,omitempty"`
}

RunLockHolder is one kernel-measured holder of the lock file (from /proc/locks) — the answer to "who", taken from the kernel rather than from a self-report.

type RunLockOptions added in v0.7.0

type RunLockOptions struct {
	// Client is the driver to derive key/path through (its runlock-path verb is
	// the single derivation authority). Required.
	Client *Client
	// Wait bounds the acquire. 0 = a single non-blocking try (R-6 report-only
	// mode). The SHAPE is contract — always bounded, always loud, always
	// overridable. The NUMBERS (30 s interactive / 10 min batch / cron try-only)
	// are consumer defaults and belong to m-cli and v-pkg, not here (decision #3).
	Wait time.Duration
	// Consumer is narrative for the holder record: "m test",
	// "v pkg install VSL.kids", …
	Consumer string
	// Dir overrides the lock home. TESTS AND FIXTURES ONLY: it decouples the
	// bracket from the real serialization domain, so a real run that sets it is
	// locking a file no other lane looks at.
	Dir string
	// OnWait fires with the measured holder as soon as the acquire blocks, and on
	// each subsequent sample. The SDK is a library and does not print; this is how
	// a wait is made loud immediately rather than silent (R-5).
	OnWait func(RunLockStatus)
	// contains filtered or unexported fields
}

RunLockOptions selects the engine instance and the acquire behavior (§6a).

type RunLockRecord added in v0.7.0

type RunLockRecord struct {
	V   int    `json:"v"`
	Key string `json:"key"`
	// TokenSHA256 is the hex SHA-256 of the run token — the token itself is NEVER
	// written. A joiner gets the token by process inheritance (env), never by
	// reading this file, so a "helpful" script cannot scrape ownership. This is
	// anti-footgun, not authentication (§9: malice is out of the threat model).
	TokenSHA256 string     `json:"tokenSHA256"`
	PID         int        `json:"pid"`
	PGID        int        `json:"pgid"`
	Host        string     `json:"host"`
	User        string     `json:"user,omitempty"`
	Consumer    string     `json:"consumer,omitempty"`
	StartedAt   time.Time  `json:"startedAt"`
	ReleasedAt  *time.Time `json:"releasedAt,omitempty"`
}

RunLockRecord is the holder record written into the lock file (§6b). It is NARRATIVE: used for messages and for token verification, never as evidence of held-ness (flock is that).

type RunLockRef added in v0.7.0

type RunLockRef struct {
	Key  string `json:"key"`
	Path string `json:"path"`
}

RunLockRef is the `meta runlock-path` payload: the derived key and lock-file path for a connection. The DRIVER is the single derivation authority (R-3), so bash, m-cli, v-pkg and both drivers cannot disagree about the key for the same target — two lanes "locking" different keys for one container is a silent failure of mutual exclusion.

func DeriveRunLockRef added in v0.7.0

func DeriveRunLockRef(dir, key string) (RunLockRef, error)

DeriveRunLockRef composes the lock ref for a key. dir is the tests-only override; empty means the default home. This is the ONE implementation of key→path, which each driver calls to answer `meta runlock-path` — so no two lanes can compose different paths for the same engine. It is also where the driver argv --run-lock-dir lands, so the production gate fires here inside the driver process itself.

type RunLockStatus added in v0.7.0

type RunLockStatus struct {
	Key     string          `json:"key"`
	Path    string          `json:"path"`
	State   string          `json:"state"`
	Holders []RunLockHolder `json:"holders,omitempty"`
	Record  *RunLockRecord  `json:"record,omitempty"`
}

RunLockStatus is the `meta runlock-status` payload: the three trust layers, in order — flock (truth of held-ness), /proc/locks (measurement of who), the record (narrative).

func ProbeRunLock added in v0.7.0

func ProbeRunLock(ref RunLockRef) (RunLockStatus, error)

ProbeRunLock measures the lock WITHOUT acquiring it: a single LOCK_EX|LOCK_NB attempt (released immediately if it succeeds), then /proc/locks for who, then the record for narrative. The probe never keeps what it takes — if it did, the first in-bracket driver call would lock the engine against its own holder.

func (RunLockStatus) Describe added in v0.7.0

func (s RunLockStatus) Describe() string

Describe renders the one-line holder narrative a waiter prints IMMEDIATELY on block (R-5) — a wait is never silent.

func (RunLockStatus) Held added in v0.7.0

func (s RunLockStatus) Held() bool

Held reports whether a bracket is live on this engine.

func (RunLockStatus) StoppedHolderTriage added in v0.7.0

func (s RunLockStatus) StoppedHolderTriage() string

StoppedHolderTriage renders the mechanized triage for a STOPPED holder. This is the suspended-engine-writer playbook retired OUT of memory and INTO the tool: a 6 h manual incident becomes a first-wait diagnostic. The tool diagnoses; the human kills. There is no auto-break, no TTL expiry and no lease steal, in v1 or ever — breaking a lock you wrongly believe dead admits a second writer, which is strictly worse than waiting (R-5).

type StateResult

type StateResult struct {
	State    string `json:"state"`
	Endpoint string `json:"endpoint,omitempty"`
}

StateResult is the lifecycle up/down/restart payload: the resulting state (e.g. "started"/"stopped"/"attached") and, where meaningful, the endpoint.

type Status

type Status struct {
	Transport  string   `json:"transport"`
	Running    bool     `json:"running"`
	Healthy    bool     `json:"healthy"`
	Version    string   `json:"version,omitempty"`
	Namespaces []string `json:"namespaces,omitempty"`
	LatencyMs  int64    `json:"latencyMs"`
	Endpoint   string   `json:"endpoint,omitempty"`
}

Status is the `lifecycle status` payload (driver-contract.md §5.1) — the liveness/readiness snapshot plus engine identity. Namespaces/Version/Endpoint are omitempty so a driver that cannot cheaply report one simply omits it.

type SweepResult added in v0.5.0

type SweepResult struct {
	Swept []string `json:"swept"`
}

SweepResult reports the stale run-staging directories (mtest-*) a `exec sweep` removed from the engine's primary source dir, so the caller can surface what self-healed (no silent cleanup).

type Transport

type Transport interface {
	// Health is the readiness/liveness probe behind `lifecycle status --probe`
	// and `wait` (driver-contract.md §3, plan §3). YDB: `%XCMD 'write 1'` → 1;
	// IRIS remote: GET /api/atelier/v1/ → 200 + version.
	Health(ctx context.Context) (Health, error)

	// Load stages routine source and compiles it (exec.load: stage + compile).
	// YDB: copy .m onto $ydb_routines (compile is implicit on first run);
	// IRIS: $SYSTEM.OBJ.Load(path,"ck") / Atelier PUT + action/compile.
	Load(ctx context.Context, req LoadRequest) (LoadResult, error)

	// Exec runs an entryref, evaluates a command, or runs a direct-mode script
	// (exec.run / exec.eval). On a compile/runtime fault it returns the fault in
	// ExecResult.EngineError, NOT as a Go error — the fault is data (§7); a Go
	// error means the transport itself failed (could not reach/launch).
	Exec(ctx context.Context, req ExecRequest) (ExecResult, error)

	// ReadGlobal reads a global node, or a subtree per Depth/Order — data.get /
	// data.query, and the result-global read that backs exec/cover orchestration.
	ReadGlobal(ctx context.Context, req GlobalRef) (GlobalNode, error)

	// SetGlobal sets a single global node (data.set), used to seed fixtures. It
	// is a first-class verb (not Exec of a "set" command) so the IRIS remote
	// substrate can route it through a parameterized, role-gated runner method
	// rather than splicing values into ObjectScript.
	SetGlobal(ctx context.Context, ref, value string) error
}

Transport is the frozen verb-level seam between a driver's vendor logic and its engine. It is deliberately NOT a low-level run(argv): the two driver shapes it must fit cannot share an argv seam (risk B1) —

  • m-ydb local/docker: pipe M into a `yottadb` session (stdin → stdout), compile implicit;
  • m-iris local/docker: pipe ObjectScript into `iris session -U NS`, compile via $SYSTEM.OBJ.Load; m-iris remote: Atelier PUT + action/compile + a SQL action/query into a role-gated runner class — NO raw "run" endpoint, no stdout, results returned through a result global the transport reads.

A verb-level interface lets each transport implement its own strategy while the rest of the driver stays transport-agnostic. Frozen at the Phase-0 checkpoint against both shapes.

type UnstageResult added in v0.5.0

type UnstageResult struct {
	Removed string `json:"removed"`
}

UnstageResult reports the run staging directory `exec unstage` removed (the connection's --stage-dir name).

type VerbClass added in v0.7.0

type VerbClass string

VerbClass is a contract verb's engine-state class.

const (
	// ClassMutating — the verb can change engine state (routines, globals,
	// namespace) or the instance's existence. A bracket token is required.
	ClassMutating VerbClass = "mutating"

	// ClassRead — the verb reads engine state whose torn view poisons a consumer.
	// A token is required, the same as mutating: owner decision #2 is STRICT,
	// because a snapshot racing a writer is exactly the torn pre-image that the
	// v-pkg F2 incident then restored as truth (audit V2). Reading a busy engine
	// waits; diagnosis is unaffected, since the control class stays exempt.
	ClassRead VerbClass = "read"

	// ClassStatus — instance metadata only: no engine state is read or written.
	// No token.
	ClassStatus VerbClass = "status"

	// ClassControl — the DECLARED escape hatch (§R-7), not a silent bypass: verbs
	// whose purpose is to unwedge or observe a run. No token, by design, for two
	// load-bearing reasons:
	//
	//   - the remedy for a stuck run must never queue behind the stuck run (the
	//     historical reason m-iris's per-call lock had to be bypassed by Abort);
	//   - AcquireRunLock bootstraps by calling the driver's runlock-path to learn
	//     the key/path, so a token-gated runlock-* could never be reached before a
	//     bracket exists — the bracket could never be taken at all (§7.3).
	ClassControl VerbClass = "control"
)

func ClassOf added in v0.7.0

func ClassOf(axis, verb string) (VerbClass, bool)

ClassOf returns the class of an axis+verb. ok is false when the verb is not in the registry — callers MUST treat that as a red (conformance does), never as a permissive default: an unclassified verb is an unenforced hole.

func (VerbClass) RequiresRunLock added in v0.7.0

func (c VerbClass) RequiresRunLock() bool

RequiresRunLock reports whether a verb of this class may run only inside a proven run-lock bracket.

Directories

Path Synopsis
cmd
m-driver-conformance command
Command m-driver-conformance is the executable contract gate (driver-contract.md §9): it drives a driver binary over the subprocess + JSON-envelope seam and reports whether the driver conforms.
Command m-driver-conformance is the executable contract gate (driver-contract.md §9): it drives a driver binary over the subprocess + JSON-envelope seam and reports whether the driver conforms.
Package conformance is the executable definition of the m engine-driver contract (driver-contract.md §9).
Package conformance is the executable definition of the m engine-driver contract (driver-contract.md §9).

Jump to

Keyboard shortcuts

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