Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ resumable programs.**
> The skill yields the next typed operation. The coding agent performs it
> and resumes the skill.

Write control flow in Go. Yield user questions, agent tasks, and commands
to the coding agent. Resume from the result. No custom agent runtime
required — the agent only runs a CLI and follows envelopes.
Write control flow in TypeScript, Python, Go, or Rust. Yield user
questions, agent tasks, and commands to the coding agent. Resume from the
result. No custom agent runtime required — the agent only runs a CLI and
follows envelopes.

A skill keeps its thin `SKILL.md` (so it works wherever skills work today)
and moves the part prose loses under context pressure — order, branching,
Expand Down Expand Up @@ -59,16 +60,41 @@ four languages and asserts identical observable protocol behavior.
Non-Go skills declare their runner in `skill.json`:
`{"run": ["node", "main.ts"]}`.

Already have prose skills? `examples/convert-skill` is a converter —
itself a Yield skill — that extracts the implicit flow from an existing
`SKILL.md`, asks you which language you want, has the model write the
program, and completes only when the generated skill passes its own
fixture run. A conversion that was never executed is never "done".
## Ten workflows, every language

The [example library](examples/library/) recreates ten common coding-agent
workflows independently in all four SDKs: branch review, failure
investigation, web QA, package release, issue triage, CI repair, dependency
upgrade, database migration, security audit, and iOS publishing.

Each language has the same workflow, a thin `SKILL.md`, and a scripted
fixture. Start from the work you already do instead of starting from a
framework tutorial.

## Documentation

Start with the [ten-minute TypeScript quickstart](docs/quickstart.md), then
use the documentation by job:

- [primitive guides](docs/primitives/README.md) — commands, model work,
human input, evidence gates, and outcomes;
- [tutorials](docs/tutorials/README.md) — review, approval, environment
repair, bounded debugging, and migration;
- [examples](docs/examples.md) — working programs in all four languages;
- [convert an existing skill](docs/convert-existing-skill.md) — move
control flow into code without claiming that fixture execution proves
every reading of the original prose;
- [CLI and runtime reference](docs/reference/cli.md).

## Try it

```
go build -o yskill ./cmd/yskill
./yskill test examples/library/typescript/review-branch
./yskill test examples/library/python/review-branch
./yskill test examples/library/go/review-branch
./yskill test examples/library/rust/review-branch
YSKILL="$PWD/yskill" bash ./examples/library/test-all.sh
./yskill test examples/investigate # Go: scripted fixture run to completion
./yskill test examples/release-checklist # TypeScript (Node >= 23.6)
./yskill test examples/env-doctor # Python 3.10+
Expand Down
215 changes: 202 additions & 13 deletions UPSTREAM.json

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Yield documentation

You already know the workflow. You may be repeating it in a prompt:

> Run the checks. Review the diff. Stop if anything critical remains. Ask me
> before publishing. If the session ends, start again without losing our place.

Yield lets you keep the useful words and put the order in normal code. The
coding agent still investigates, reviews, edits, and explains. Your program
decides which operation comes next, what evidence must exist, and when the run
is finished.

## Start here

1. [Build and run your first skill](quickstart.md) — a TypeScript workflow you
can test in about ten minutes.
2. [Learn the primitives](primitives/README.md) — commands, model work, human
input, gates, and honest outcomes.
3. [Follow a complete tutorial](tutorials/README.md) — review, approval,
environment repair, bounded debugging, and migration.
4. [Browse the examples](examples.md) — working programs in Go, TypeScript,
Python, and Rust.
5. [Convert an existing prose skill](convert-existing-skill.md) — use Yield's
verified converter after you understand one ordinary workflow.

## The split to remember

| Put in code | Leave with the model |
|---|---|
| order and branching | investigation and judgment |
| retry limits | reading unfamiliar code |
| commands that must really run | proposing changes |
| approval points | writing explanations |
| evidence required to finish | interpreting evidence |

This is not a new agent runtime. A thin `SKILL.md` starts the program, the
program emits one typed operation, and the coding agent performs that operation
through its normal interface. Yield records the response and resumes from the
next unanswered operation.

## Reference

- [CLI commands](reference/cli.md)
- [Run, pause, resume, and replay](reference/execution-model.md)
- [The four SDKs](reference/sdk-parity.md)
- [Guarantees and limits](reference/guarantees.md)
66 changes: 66 additions & 0 deletions docs/convert-existing-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Convert an existing prose skill

Use conversion after you have run one ordinary Yield workflow. Conversion is a
separate job from the runtime itself: the converter helps extract and encode a
policy; Yield then executes and verifies the resulting program.

The repository includes [`examples/convert-skill`](../examples/convert-skill/),
a converter that is itself a Yield skill.

## What moves, and what stays

Keep these in the thin `SKILL.md`:

- the goal and when the skill should be used;
- domain context the model needs;
- judgment criteria and useful examples;
- the instruction to start and resume the Yield program.

Move these into the program:

- required order;
- branches and retry limits;
- commands whose output must be observed;
- approval points;
- claims required for completion;
- `Blocked` and `Refused` outcomes.

## Run the converter

From a checkout of the public repository:

```bash
go build -o /tmp/yskill ./cmd/yskill
cd examples/convert-skill
YSKILL=/tmp/yskill /tmp/yskill run .
```

The workflow asks for:

1. the source directory containing `SKILL.md`;
2. a target language;
3. a destination directory.

The coding agent extracts the implicit flow and writes the generated files. The
converter then runs the generated skill's own fixtures under `yskill test`. It
allows two bounded repair attempts and returns `Blocked` if the fixture run
still fails.

## What “verified” means here

The converter verifies that the generated program executes its declared fixture
path. It does **not** prove that the extracted policy is behaviorally equivalent
to every reading of the original prose.

Review the conversion as a policy change:

- map every load-bearing source instruction to code, retained model judgment,
or an explicit exclusion;
- add a positive fixture and at least one negative or refusal path;
- test bypass attempts and failure states;
- replay a completed run to check determinism;
- keep performance or token-reduction claims separate from runtime correctness.

The implemented safety comparison is documented in
[`locus-converter.md`](locus-converter.md). Its narrow conclusion is that the
converter cannot report success before the generated fixture run passes.
62 changes: 62 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Example library

The library contains ten common coding-agent workflows. Every workflow is
implemented in TypeScript, Python, Go, and Rust, so the first choice is your
repository's language—not which example happens to exist.

| Workflow | Control flow moved into code |
|---|---|
| [Review a branch](../examples/library/typescript/review-branch/) | checks, review, zero-critical gate |
| [Investigate a failure](../examples/library/typescript/investigate-failure/) | evidence, diagnosis, supported cause |
| [QA a web change](../examples/library/typescript/qa-web-change/) | build, changed-route QA, no-blocker gate |
| [Release a package](../examples/library/typescript/release-package/) | tests, review, approval, publish, verify |
| [Triage an issue](../examples/library/typescript/triage-issue/) | read, classify, one next action |
| [Repair CI](../examples/library/typescript/repair-ci/) | failed log, supported repair, rerun |
| [Upgrade a dependency](../examples/library/typescript/upgrade-dependency/) | baseline, compatibility review, approval, update, tests |
| [Run a database migration](../examples/library/typescript/migrate-database/) | dry-run, risk review, approval, apply, verify |
| [Audit security](../examples/library/typescript/audit-security/) | mechanical scans, trust-boundary review, zero-critical gate |
| [Publish an iOS build](../examples/library/typescript/publish-ios/) | archive, metadata review, approval, upload, processing check |

Change the language segment in any link to python, go, or rust. Source files
are grouped separately for fast browsing:

- [TypeScript](../examples/library/typescript/src/)
- [Python](../examples/library/python/src/)
- [Go](../examples/library/go/src/)
- [Rust](../examples/library/rust/src/bin/)

Run all forty fixtures:

go build -o /tmp/yskill ./cmd/yskill
YSKILL=/tmp/yskill bash ./examples/library/test-all.sh

The included commands produce harmless evidence so the examples run in this
repository. Replace them with project commands before adopting a workflow.

## Complete walkthroughs

These examples show longer programs with a thin `SKILL.md` and scripted
responses under `fixtures/responses.json`.

| Example | Language | Pattern |
|---|---|---|
| [`release-checklist`](../examples/release-checklist/) | TypeScript | approval, build, model-authored notes, publish, verify |
| [`env-doctor`](../examples/env-doctor/) | Python | probe, diagnose, wait for a person, recheck |
| [`investigate`](../examples/investigate/) | Go | structured hypotheses, real probes, bounded attempts |
| [`data-migration`](../examples/data-migration/) | Rust | dry-run, approval, apply, verify |
| [`convert-skill`](../examples/convert-skill/) | Go | extract a prose workflow, generate code, execute its fixtures |

From the repository root:

```bash
go build -o /tmp/yskill ./cmd/yskill
/tmp/yskill test examples/release-checklist
/tmp/yskill test examples/env-doctor
/tmp/yskill test examples/investigate
/tmp/yskill test examples/data-migration
YSKILL=/tmp/yskill /tmp/yskill test examples/convert-skill
```

When adapting an example, change the repository-specific commands and model
instructions. Keep stable operation IDs for existing steps so saved runs can
replay them.
13 changes: 13 additions & 0 deletions docs/locus-conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ discharges the whole program's obligations. Models and derivations live in
| `sdk-contract.json` | control.nonblockingness | **nonblocking** — every SDK execution reaches an emit |
| `divergence-in-sdk.json` vs `divergence-supervisor-only.json` | verification.safety-reachability (rival designs, `drv-ad50b13e…`) | **decided** — in-SDK per-step checking satisfies `forbidden-unreachable`; supervisor-only is rejected with the verbatim trace `op_drifts → consume_unchecked → CONSUMED_MISMATCHED` |

The feature-extension boundary also passed
`practice.boundary-conformance`. Its control law is deliberately small:
SDK stdout becomes engine authority only after the protocol package admits
exactly one complete `request`, `terminal`, or `diverged` variant. Unknown
fields and malformed, missing, or ambiguous variants fail before dispatch.
The canonical IR uses the same exact-one shape, while TypeScript and Rust
encode it with closed surface types.

The decided comparison is why per-step digest comparison is a MANDATORY
part of the SDK contract in every language, not an optional nicety: without
it, a drifted operation silently consumes a recorded response meant for a
Expand Down Expand Up @@ -42,6 +50,11 @@ scenario matrix and what each observes:
| `TestGuardRefusals` | schema-invalid, duplicate-rewrite, wrong-run refusals through the live engine |
| `TestDivergenceFailsLoudlyEverywhere` | the decided design: a tampered recorded operation is detected by every SDK at replay |

`internal/protocol/ir_test.go` adds the feature-upgrade gate: positive and
negative outputs must receive the same decision from protocol admission and
the canonical schema. `internal/engine/engine_test.go` proves ambiguous SDK
output is refused at the real subprocess boundary.

Languages whose toolchain is missing are skipped locally; CI provides all
four (`.github/workflows/yield-lab.yml`).

Expand Down
6 changes: 3 additions & 3 deletions docs/locus/sdk-contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"evidence": [
{
"path": "labs/22-yield/yield/sdk/yield/yield.go",
"note": "Go SDK: step() replay/compare/emit, Main() terminal emission \u2014 the behavior TS/Python SDKs must refine"
"note": "Go SDK: step() replay/compare/emit and Main() terminal emission \u2014 the reference behavior all four SDKs refine"
},
{
"path": "labs/22-yield/yield/internal/engine/engine.go",
Expand Down Expand Up @@ -225,7 +225,7 @@
"selector": "marked"
},
"unknowns": [
"The TS and Python SDKs are not yet implemented; this contract is what they must exhibit, discharged by running each example skill through yskill test.",
"A future output variant must be added coherently to every SDK surface, the canonical IR, protocol admission, engine dispatch, and conformance fixtures.",
"Determinism between yields is the program author's obligation in every language; the contract detects divergence, it cannot prevent nondeterminism."
]
}
}
30 changes: 30 additions & 0 deletions docs/primitives/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Yield primitives

Yield has a deliberately small API. Each primitive has one clear owner.

| Primitive | What it does | Who performs it |
|---|---|---|
| [`RunCommand`](run-command.md) | Runs a command and records its real output | `yskill` |
| [`AgentTask`](agent-task.md) | Requests model judgment with an optional JSON schema | coding agent |
| [`AskUser`](ask-user.md) | Pauses for a human answer | coding agent and user |
| [`Require`](require.md) | Prevents completion unless a claim passes | skill program |
| [Outcomes](outcomes.md) | Completes, blocks, or refuses with a recorded reason | skill program |

Ordinary language features provide the rest. Use `if` for choices, `for` or
`while` for bounded retries, functions for reusable flows, and your language's
types for local data.

## Names in each SDK

| Meaning | TypeScript | Python | Go | Rust |
|---|---|---|---|---|
| ask a person | `ctx.askUser` | `ctx.ask_user` | `ctx.AskUser` | `ctx.ask_user` |
| ask the model | `ctx.agentTask` | `ctx.agent_task` | `ctx.AgentTask` | `ctx.agent_task` |
| run a command | `ctx.runCommand` | `ctx.run_command` | `ctx.RunCommand` | `ctx.run_command` |
| enforce a claim | `ctx.require` | `ctx.require` | `ctx.Require` | `ctx.require` |
| finish | `return value` | `return value` | `ctx.Complete` | `Ok(value)` |
| cannot continue | `ctx.blocked` | `ctx.blocked` | `ctx.Blocked` | `Err(ctx.blocked(...))` |
| decline to continue | `ctx.refused` | `ctx.refused` | `ctx.Refused` | `Err(ctx.refused(...))` |

All four SDKs emit the same `yield.v1` protocol. Choose the language that best
fits the repository containing the skill.
40 changes: 40 additions & 0 deletions docs/primitives/agent-task.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# `AgentTask`: keep judgment with the model

Use `AgentTask` for work that needs interpretation: reviewing a diff,
diagnosing a failure, comparing designs, extracting a policy, or proposing a
fix.

```ts
type Diagnosis = { cause: string; confidence: number };

const diagnosis = ctx.agentTask<Diagnosis>(
"diagnose",
"Find the most likely cause of this test failure.",
{ stdout: test.stdout, stderr: test.stderr },
{
type: "object",
required: ["cause", "confidence"],
properties: {
cause: { type: "string" },
confidence: { type: "number" },
},
},
);
```

The arguments are:

1. a stable operation ID;
2. the instruction;
3. optional structured context;
4. an optional JSON Schema for the response.

The supervisor validates the response schema before accepting it. Schema-valid
does not mean true; use `RunCommand`, human approval, or another explicit check
when the workflow needs stronger evidence.

## Common mistake

Do not put retry order, approval rules, or completion policy inside the
instruction. Keep those rules in the surrounding program where they can be
replayed and tested.
25 changes: 25 additions & 0 deletions docs/primitives/ask-user.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# `AskUser`: pause for a person

Use `AskUser` when a person must choose, provide missing information, or approve
an irreversible action.

```ts
const answer = ctx.askUser("approve", "Publish this release?", [
{ value: "yes", label: "Publish" },
{ value: "no", label: "Stop" },
]);

if (answer !== "yes") ctx.refused("the user declined publication");
```

The coding agent asks through its normal interface. Yield records the answer
and replays it when the program starts again. The run can wait on disk between
the question and the answer.

Use a closed list of options when only specific values are valid. Use a free
answer when the person needs to provide a path, identifier, or explanation.

## Common mistake

Do not ask for approval after the command has already changed the system. Put
the question before the effect, and use `Refused` when the person says no.
Loading
Loading