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
- Variables
- func ClassifiedVerbs() map[string]VerbClass
- func DefaultRunLockDir() (string, error)
- func ExecRunner(ctx context.Context, name string, args []string) (stdout, stderr []byte, exit int, err error)
- func HashSelector(s string) string
- func IsEngineBusy(err error) bool
- func Locate(engine string, d LocateDeps) (string, error)
- func RequireRunLock(class VerbClass, ref RunLockRef, token string) error
- func UnclassifiedVerbs(c Caps) []string
- func UnresolvedRunLock(engine string, cause error) error
- func VerifyRunLock(ref RunLockRef, token string) error
- type Axes
- type AxisVerbs
- type Caps
- type Check
- type Client
- func (c *Client) Caps(ctx context.Context) (Caps, error)
- func (c *Client) ExecEval(ctx context.Context, command string) (ExecResult, error)
- func (c *Client) ExecRun(ctx context.Context, entryref string, args []string) (ExecResult, error)
- func (c *Client) ExecScript(ctx context.Context, script string) (ExecResult, error)
- func (c *Client) Load(ctx context.Context, paths []string) (LoadResult, error)
- func (c *Client) LoadNoCompile(ctx context.Context, paths []string) (LoadResult, error)
- func (c *Client) RunLockPath(ctx context.Context, dir string) (RunLockRef, error)
- func (c *Client) RunLockStatus(ctx context.Context) (RunLockStatus, error)
- func (c *Client) StageSweep(ctx context.Context, ttlHours int) (SweepResult, error)
- func (c *Client) Status(ctx context.Context) (Status, error)
- func (c *Client) SupportsRunLock(ctx context.Context) (bool, error)
- func (c *Client) Unstage(ctx context.Context) (UnstageResult, error)
- type CmdRunner
- type CoverResult
- type DockerError
- type DockerLocator
- type DockerRunner
- type DoctorResult
- type DriverError
- type EngineError
- type ExecRequest
- type ExecResult
- type FakeCall
- type FakeTransport
- func (f *FakeTransport) Exec(ctx context.Context, req ExecRequest) (ExecResult, error)
- func (f *FakeTransport) Health(ctx context.Context) (Health, error)
- func (f *FakeTransport) Load(ctx context.Context, req LoadRequest) (LoadResult, error)
- func (f *FakeTransport) ReadGlobal(ctx context.Context, req GlobalRef) (GlobalNode, error)
- func (f *FakeTransport) SetGlobal(ctx context.Context, ref, value string) error
- type Features
- type GlobalNode
- type GlobalRef
- type Health
- type Limits
- type LoadRequest
- type LoadResult
- type LocateDeps
- type Locus
- type LocusClass
- type RunLock
- type RunLockError
- type RunLockHolder
- type RunLockOptions
- type RunLockRecord
- type RunLockRef
- type RunLockStatus
- type StateResult
- type Status
- type SweepResult
- type Transport
- type UnstageResult
- type VerbClass
Constants ¶
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.
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.
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.
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).
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)).
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).
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 ¶
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.
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.
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
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
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
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
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
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
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).
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 (*Client) ExecEval ¶
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) ExecScript ¶ added in v0.4.0
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) LoadNoCompile ¶ added in v0.4.0
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
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
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 ¶
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
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
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
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
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
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 ¶
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 (f *FakeTransport) Exec(ctx context.Context, req ExecRequest) (ExecResult, error)
func (*FakeTransport) Load ¶
func (f *FakeTransport) Load(ctx context.Context, req LoadRequest) (LoadResult, error)
func (*FakeTransport) ReadGlobal ¶
func (f *FakeTransport) ReadGlobal(ctx context.Context, req GlobalRef) (GlobalNode, 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 ¶
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.
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
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
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
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
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
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 ¶
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
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
RequiresRunLock reports whether a verb of this class may run only inside a proven run-lock bracket.
Source Files
¶
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). |