yield

package
v0.5.2 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 4 Imported by: 0

README

Yield

Yield for Go

Move repeatable coding-agent instructions from words into Go.

Build typed, resumable workflows that stay beside the code they operate on.

Go reference Go module version Build status MIT license

Website · Documentation · pkg.go.dev · GitHub

The Go module is github.com/operatorstack/yield. Import the SDK as github.com/operatorstack/yield/sdk/yield. The installed command is yskill.

Create a workflow

1. Install Yield

Yield supports Go on macOS, Linux, and Windows. Install the public command:

mkdir -p .yield/bin
GOBIN="$PWD/.yield/bin" go install github.com/operatorstack/yield/cmd/yskill@latest
.yield/bin/yskill --version

Go downloads the tagged module through proxy.golang.org. You do not need a separate registry account or private package source.

2. Create the workflow

Create a Go workflow inside your repository:

.yield/bin/yskill init skills/investigate \
  --language go \
  --description "Collect failure evidence, test hypotheses, and report the cause."

Replace skills/investigate/main.go with this tested workflow:

package main

import (
	"encoding/json"
	"fmt"

	"github.com/operatorstack/yield/sdk/yield"
)

type hypothesis struct {
	ID              string `json:"id"`
	Statement       string `json:"statement"`
	DisproveCommand string `json:"disprove_command"`
}

type assessment struct {
	Refuted     bool   `json:"refuted"`
	CausalChain string `json:"causal_chain"`
}

const hypothesesSchema = `{
  "type": "object",
  "required": ["hypotheses"],
  "properties": {
    "hypotheses": {
      "type": "array",
      "minItems": 3,
      "items": {
        "type": "object",
        "required": ["id", "statement", "disprove_command"],
        "properties": {
          "id": {"type": "string"},
          "statement": {"type": "string"},
          "disprove_command": {"type": "string"}
        }
      }
    }
  }
}`

const assessmentSchema = `{
  "type": "object",
  "required": ["refuted"],
  "properties": {
    "refuted": {"type": "boolean"},
    "causal_chain": {"type": "string"}
  }
}`

func main() {
	yield.Main(func(ctx *yield.Context) (yield.Outcome, error) {
		evidence := ctx.AgentTask("collect-evidence",
			"Collect the observable evidence for the failure under investigation: error output, logs, recent changes. Return {\"observations\": [string]}.",
			nil, json.RawMessage(`{"type":"object","required":["observations"],"properties":{"observations":{"type":"array","items":{"type":"string"}}}}`))

		raw := ctx.AgentTask("form-hypotheses",
			"Produce at least three hypotheses explaining the evidence, ordered cheapest-to-disprove first. Each carries a shell command whose failure would disprove it.",
			json.RawMessage(evidence), json.RawMessage(hypothesesSchema))
		var hs struct {
			Hypotheses []hypothesis `json:"hypotheses"`
		}
		if err := json.Unmarshal(raw, &hs); err != nil {
			return yield.Outcome{}, err
		}

		failures := 0
		for _, h := range hs.Hypotheses {
			if failures >= 3 {
				break
			}
			result := ctx.RunCommand("probe-"+h.ID, h.DisproveCommand, 300)
			assessRaw := ctx.AgentTask("assess-"+h.ID,
				fmt.Sprintf("Hypothesis %q: %s. Given the probe result, is it refuted? If it survives, state the causal chain from root cause to observed failure.", h.ID, h.Statement),
				map[string]any{"hypothesis": h, "probe": result},
				json.RawMessage(assessmentSchema))
			var a assessment
			if err := json.Unmarshal(assessRaw, &a); err != nil {
				return yield.Outcome{}, err
			}
			if a.Refuted {
				failures++
				continue
			}
			ctx.Require(a.CausalChain != "", "the surviving hypothesis states a causal chain", a)
			return ctx.Complete(map[string]any{
				"hypothesis":   h,
				"causal_chain": a.CausalChain,
				"probe_exit":   result.ExitCode,
			})
		}
		return yield.Outcome{}, ctx.Blocked(
			fmt.Sprintf("frontier reached: %d hypotheses refuted with %d failed attempts and none surviving — new evidence is needed, not more guessing", len(hs.Hypotheses), failures))
	})
}

The generated go.mod pins the public Yield module to the installed CLI version. The generated skill.json runs the Go program.

3. Test the workflow

Use deterministic responses during tests. Save this as skills/investigate/fixtures/responses.json:

{
  "collect-evidence": {
    "observations": [
      "CI fails on ubuntu only with 'Text file busy' (exit 126)",
      "failure started after the hydrate step became concurrent",
      "macOS and windows runners are green"
    ]
  },
  "form-hypotheses": {
    "hypotheses": [
      {
        "id": "h1",
        "statement": "The runner image is missing the binary entirely",
        "disprove_command": "exit 1"
      },
      {
        "id": "h2",
        "statement": "Concurrent hydrate writes the binary while another process execs it (ETXTBSY)",
        "disprove_command": "true"
      },
      {
        "id": "h3",
        "statement": "A permissions regression strips the execute bit",
        "disprove_command": "true"
      }
    ]
  },
  "assess-h1": {
    "refuted": true
  },
  "assess-h2": {
    "refuted": false,
    "causal_chain": "concurrent hydrate holds the binary open for write -> exec of the same inode returns ETXTBSY -> shell reports exit 126 -> job fails only where hydrate and exec overlap (ubuntu)"
  }
}

Then test the workflow:

.yield/bin/yskill doctor skills/investigate --root . --test

Yield runs commands for real and supplies agent responses from the fixture. A successful test reaches completed without leaving a run journal.

4. Register the skill

Registration lets installed coding agents discover the workflow:

.yield/bin/yskill register skills/investigate --root .

Select the verified agents explicitly when you do not want automatic detection:

.yield/bin/yskill register skills/investigate --root . \
  --agent cursor,codex,claude-code

The generated adapters point back to skills/investigate. They do not copy the workflow or install its dependencies again.

5. Run the skill

Start a new coding-agent session so it discovers the registered skill. Where slash skills are supported, run:

/investigate

Otherwise, ask the agent in plain language:

Use the investigate skill to diagnose this failure.

The agent follows the adapter, starts the canonical Go workflow, and supplies each required agent response.

How Yield runs and resumes

  1. Your Go function emits one typed operation.
  2. Yield records the request and exits. It does not run a daemon.
  3. The coding agent, user, or CLI supplies the result.
  4. Yield replays the function from its journal until it reaches the next operation.

Replay must produce the same operation sequence. Yield reports divergence instead of giving a recorded response to a different operation.

Where AgentTask fits

ctx.AgentTask() delegates one bounded judgment to the coding agent. Pass important evidence explicitly, as this workflow passes its earlier structured results. With its schema, Yield checks the returned JSON shape before the workflow continues; it does not prove the diagnosis is correct. Host workspace and conversation access are host-dependent.

Go primitive Purpose
ctx.RunCommand() Execute a command and record its exit code and output.
ctx.AgentTask() Delegate one bounded judgment; an optional schema validates the result.
ctx.AskUser() Request an explicit human decision.
ctx.Require() Bind a required claim to recorded evidence.
ctx.Blocked() / ctx.Refused() Stop honestly when work cannot or must not continue.

See the Go reference, primitive guides, and CLI reference for the complete contract.

Guarantees and limits

Yield provides deterministic control flow, typed requests and responses, persistent run state, replay with divergence detection, stale and duplicate response rejection, and evidence-bound completion.

Schema validity is not truth. Yield cannot prove that a coding agent performed only the requested work. RunCommand is different: the Yield CLI executes the command, so its recorded exit code and output are observed facts.

Programs must remain deterministic between operations. Do not read clocks, random values, environment variables, or changing files to choose the next operation. Cross those boundaries through a Yield operation instead.

Yield is not a daemon, hosted runtime, workflow DSL, marketplace, coding-agent replacement, or permission sandbox. Your operating system, repository, and coding-agent permissions remain the security boundary.

Optional developer helper

Installing the Go runtime does not create skills or coding-agent adapters. After learning the manual workflow above, install guided assistance explicitly:

.yield/bin/yskill helper install --root . --language go

Review the plan and restart the coding agent after installation. The helper can teach, create, convert, check, repair, upgrade, and register workflows. yskill bootstrap remains a compatibility alias.

Coding-agent support

Yield verifies adapters for Cursor, Codex, and Claude Code. Registry-backed project paths are available for other coding agents. See the agent setup guide.

Source and support

Yield is available under the MIT License.

Documentation

Overview

Package yield is the skill-program SDK. A skill is an ordinary Go main package that calls Main with a deterministic program. Every side effect crosses a yielded primitive; code between yields must be deterministic — that is what makes replay-based resume sound.

Execution model (deterministic re-execution): on every run/resume the program re-executes from the top. Recorded responses are fed back in order; at the first unanswered operation the SDK emits a yield.v1 request envelope on stdout and exits. If a replayed step produces a different operation than the journal recorded, the SDK reports divergence and the run fails loudly — it never silently forks.

Index

Constants

View Source
const EnvJournal = "YIELD_JOURNAL"

EnvJournal names the environment variable pointing at the journal file the supervisor (yskill) hands to the subprocess.

Variables

This section is empty.

Functions

func Main

func Main(program func(*Context) (Outcome, error))

Main runs a skill program under the supervisor protocol. It reads the journal named by YIELD_JOURNAL, executes the program, and emits exactly one ProgramOutput on stdout.

Types

type BlockedError

type BlockedError struct{ Reason string }

Blocked ends the run at a true frontier, explicitly.

func (*BlockedError) Error

func (e *BlockedError) Error() string

type Context

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

Context carries the replay cursor and the primitives.

func (*Context) AgentTask

func (c *Context) AgentTask(id, instruction string, contextData any, schema json.RawMessage) json.RawMessage

AgentTask delegates reasoning to the model. schema (JSON Schema bytes, may be nil) is enforced by the supervisor on resume; the returned raw message is schema-valid by construction.

func (*Context) AskUser

func (c *Context) AskUser(id, question string, options ...Option) string

AskUser yields a question to be asked through the host's normal interface and returns the selected value on resume.

func (*Context) Blocked

func (c *Context) Blocked(reason string) error

Blocked returns the terminal blocked error.

func (*Context) Complete

func (c *Context) Complete(result any) (Outcome, error)

Complete finishes the run with a result; evidence is the requirement trail accumulated via Require.

func (*Context) Refused

func (c *Context) Refused(reason string) error

Refused returns the terminal refused error.

func (*Context) Require

func (c *Context) Require(ok bool, claim string, evidence any)

Require binds a claim to evidence. A failed requirement terminates the program immediately with a requirement_failed outcome; completion is structurally unreachable past a failed requirement.

func (*Context) RunCommand

func (c *Context) RunCommand(id, command string, timeoutSeconds int) protocol.CommandResult

RunCommand yields a command that yskill executes itself — the result is observed fact, not the agent's account of it.

type Option added in v0.1.23

type Option struct {
	Value string
	Label string
}

Option is one allowed answer to an AskUser question.

type Outcome

type Outcome struct {
	Result any
}

Outcome is what a program returns on success.

type RefusedError

type RefusedError struct{ Reason string }

Refused ends the run because the skill declines to proceed.

func (*RefusedError) Error

func (e *RefusedError) Error() string

Jump to

Keyboard shortcuts

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