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
1 change: 1 addition & 0 deletions .github/workflows/yield-lab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
/tmp/yskill test examples/release-checklist
/tmp/yskill test examples/env-doctor
/tmp/yskill test examples/data-migration
YSKILL=/tmp/yskill /tmp/yskill test examples/convert-skill

# Shift-left projection gate: materialize the public surface exactly as
# operatorstack/yield would receive it and compile+test it, so a broken
Expand Down
6 changes: 6 additions & 0 deletions labs/22-yield/public-readme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ 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".

## Try it

```
Expand Down
39 changes: 39 additions & 0 deletions labs/22-yield/yield/docs/locus-converter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Locus derivation — the skill converter

`examples/convert-skill` turns an existing prose `SKILL.md` into a Yield
program in the operator's chosen language (Go / TypeScript / Python /
Rust). The converter is itself a Yield skill — the pipeline that makes
skills reliable is the pipeline that converts them.

## Verdicts (models in `docs/locus/`)

| model | operator | verdict |
|---|---|---|
| `convert-verified.json` | control.supervisory-rw | **controllable**, no violations — shipping an unverified conversion is preventable |
| `convert-verified.json` | control.nonblockingness | **nonblocking** — every conversion ends at `SHIPPED_VERIFIED` or an honest `REPORTED_BLOCKED` |
| `convert-verified.json` vs `convert-transcribed.json` | verification.safety-reachability (rival designs, `drv-…`) | **decided** — the executed-verification design satisfies `forbidden-unreachable`; completing on the model's transcription is rejected with the verbatim trace `extract_flow → pick_language → generate_program → complete_untested → SHIPPED_UNVERIFIED` |

The decided comparison is the design's spine: **a conversion that was
never executed is never "done".** `complete_untested` stays in the
alphabet with no transition — the program's `Require(test.exit_code == 0)`
is the refusing mechanism, and the two-attempt repair loop ends in an
honest `Blocked`, keeping the pipeline nonblocking.

## Division of labor

| code owns | model owns |
|---|---|
| pipeline order (read → extract → choose → generate → verify) | reading the prose and extracting the implicit flow |
| the language menu (`ask_user`, closed set) | writing the program, thin SKILL.md, runner manifest, fixtures |
| the repair bound (≤ 2 attempts) | repairing a failing generation |
| the evidence gate (`yskill test` exit code, observed by the supervisor) | — |

## Verified end-to-end without a model

`fixtures/responses.json` scripts a conversion whose destination is an
existing valid skill, so `yskill test examples/convert-skill` exercises
the full machinery — including the **nested** `yskill test` of the
"generated" skill (`${YSKILL:-yskill}` lets harnesses pin the binary).
What the scripted run cannot exercise is the model actually writing good
code; that is exactly the part the evidence gate exists to check at live
time.
161 changes: 161 additions & 0 deletions labs/22-yield/yield/docs/locus/convert-transcribed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{
"schema_version": 1,
"id": "convert-skill-transcribed-v1",
"subject": "Yield skill converter pipeline: extract flow from prose SKILL.md, operator picks the target language, the model generates the program, and the converter completes on the model's word that the code is correct.",
"evidence": [
{
"path": "labs/22-yield/yield/cmd/yskill/main.go",
"note": "design B: no yskill test run; the generated program is returned as text and shipped on transcription"
}
],
"states": [
{
"id": "SOURCED"
},
{
"id": "FLOW_EXTRACTED"
},
{
"id": "LANG_CHOSEN"
},
{
"id": "GENERATED"
},
{
"id": "TEST_RUN"
},
{
"id": "TEST_PASSED"
},
{
"id": "TEST_FAILED"
},
{
"id": "SHIPPED_VERIFIED",
"marked": true
},
{
"id": "SHIPPED_UNVERIFIED"
},
{
"id": "REPORTED_BLOCKED",
"marked": true
}
],
"events": [
{
"id": "read_prose",
"controllable": true,
"observable": true
},
{
"id": "extract_flow",
"controllable": false,
"observable": true
},
{
"id": "pick_language",
"controllable": false,
"observable": true
},
{
"id": "generate_program",
"controllable": false,
"observable": true
},
{
"id": "run_generated_test",
"controllable": true,
"observable": true
},
{
"id": "test_passes",
"controllable": false,
"observable": true
},
{
"id": "test_fails",
"controllable": false,
"observable": true
},
{
"id": "regenerate",
"controllable": true,
"observable": true
},
{
"id": "complete_with_evidence",
"controllable": true,
"observable": true
},
{
"id": "complete_untested",
"controllable": true,
"observable": true
},
{
"id": "declare_blocked",
"controllable": true,
"observable": true
},
{
"id": "archive",
"controllable": false,
"observable": true
}
],
"transitions": [
{
"from": "SOURCED",
"event": "extract_flow",
"to": "FLOW_EXTRACTED",
"evidence": [
0
]
},
{
"from": "FLOW_EXTRACTED",
"event": "pick_language",
"to": "LANG_CHOSEN",
"evidence": [
0
]
},
{
"from": "LANG_CHOSEN",
"event": "generate_program",
"to": "GENERATED",
"evidence": [
0
]
},
{
"from": "GENERATED",
"event": "complete_untested",
"to": "SHIPPED_UNVERIFIED",
"evidence": [
0
]
},
{
"from": "SHIPPED_UNVERIFIED",
"event": "archive",
"to": "SHIPPED_UNVERIFIED",
"evidence": [
0
]
}
],
"spec": {
"description": "A converted skill must never ship without its fixture run passing under the real supervisor.",
"forbidden_states": [
"SHIPPED_UNVERIFIED"
]
},
"targets": {
"selector": "marked"
},
"unknowns": [
"Design B ships whatever the model asserts compiles; nothing observes the generated program executing."
]
}
57 changes: 57 additions & 0 deletions labs/22-yield/yield/docs/locus/convert-verified.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"schema_version": 1,
"id": "convert-skill-verified-v1",
"subject": "Yield skill converter pipeline: extract flow from prose SKILL.md, operator picks the target language, the model generates the program, and the converter completes only after the generated skill passes its own fixture run under yskill test.",
"evidence": [
{ "path": "labs/22-yield/yield/cmd/yskill/main.go", "note": "yskill test: scripted fixture run of a skill directory; exit code is observed fact" },
{ "path": "labs/22-yield/yield/internal/guard/guard.go", "note": "evidence-bound completion: a failed requirement prevents run.completed" }
],
"states": [
{ "id": "SOURCED" },
{ "id": "FLOW_EXTRACTED" },
{ "id": "LANG_CHOSEN" },
{ "id": "GENERATED" },
{ "id": "TEST_RUN" },
{ "id": "TEST_PASSED" },
{ "id": "TEST_FAILED" },
{ "id": "SHIPPED_VERIFIED", "marked": true },
{ "id": "SHIPPED_UNVERIFIED" },
{ "id": "REPORTED_BLOCKED", "marked": true }
],
"events": [
{ "id": "read_prose", "controllable": true, "observable": true },
{ "id": "extract_flow", "controllable": false, "observable": true },
{ "id": "pick_language", "controllable": false, "observable": true },
{ "id": "generate_program", "controllable": false, "observable": true },
{ "id": "run_generated_test", "controllable": true, "observable": true },
{ "id": "test_passes", "controllable": false, "observable": true },
{ "id": "test_fails", "controllable": false, "observable": true },
{ "id": "regenerate", "controllable": true, "observable": true },
{ "id": "complete_with_evidence", "controllable": true, "observable": true },
{ "id": "complete_untested", "controllable": true, "observable": true },
{ "id": "declare_blocked", "controllable": true, "observable": true },
{ "id": "archive", "controllable": false, "observable": true }
],
"transitions": [
{ "from": "SOURCED", "event": "extract_flow", "to": "FLOW_EXTRACTED", "evidence": [0] },
{ "from": "FLOW_EXTRACTED", "event": "pick_language", "to": "LANG_CHOSEN", "evidence": [0] },
{ "from": "LANG_CHOSEN", "event": "generate_program", "to": "GENERATED", "evidence": [0] },
{ "from": "GENERATED", "event": "run_generated_test", "to": "TEST_RUN", "evidence": [0] },
{ "from": "TEST_RUN", "event": "test_passes", "to": "TEST_PASSED", "evidence": [0] },
{ "from": "TEST_RUN", "event": "test_fails", "to": "TEST_FAILED", "evidence": [0] },
{ "from": "TEST_FAILED", "event": "regenerate", "to": "GENERATED", "evidence": [0] },
{ "from": "TEST_FAILED", "event": "declare_blocked", "to": "REPORTED_BLOCKED", "evidence": [1] },
{ "from": "TEST_PASSED", "event": "complete_with_evidence", "to": "SHIPPED_VERIFIED", "evidence": [1] },
{ "from": "SHIPPED_VERIFIED", "event": "archive", "to": "SHIPPED_VERIFIED", "evidence": [0] },
{ "from": "REPORTED_BLOCKED", "event": "archive", "to": "REPORTED_BLOCKED", "evidence": [0] }
],
"spec": {
"description": "A converted skill must never ship without its fixture run passing under the real supervisor.",
"forbidden_states": ["SHIPPED_UNVERIFIED"]
},
"targets": { "selector": "marked" },
"unknowns": [
"Design A refuses complete_untested structurally: the event is in the alphabet, but the converter program offers no transition — Require(test.exit_code == 0) stands between GENERATED and completion.",
"Regeneration is bounded by a retry counter in the converter program; the model treats the bound as data, not modeled state."
]
}
Loading
Loading