Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ jobs:
sdk/rust
examples/data-migration
examples/library/rust
examples/observation/rust
- run: npm ci --ignore-scripts
- name: Run every example fixture
run: |
Expand All @@ -185,6 +186,7 @@ jobs:
"$RUNNER_TEMP/yskill" test examples/data-migration
YSKILL="$RUNNER_TEMP/yskill" "$RUNNER_TEMP/yskill" test examples/convert-skill
YSKILL="$RUNNER_TEMP/yskill" bash ./examples/library/test-all.sh
bash ./examples/observation/test-all.sh

validate:
name: Release authority and full validation
Expand Down
15 changes: 15 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ Run all forty fixtures:
The included commands produce harmless evidence so the examples run in this
repository. Replace them with project commands before adopting a skill workflow.

## Observation readers

The [RunReceipt reader examples](../examples/observation/) show how Go,
TypeScript, Python, and Rust consume the same already-materialized canonical
receipt. Each reader verifies the closed schema, canonical bytes, and receipt
digest before emitting an identical privacy-safe summary.

These are standalone post-run consumers, not skill workflows. They do not read
journals, project receipts, participate in replay, or appear in the generated
forty-example skill library.

```bash
bash ./examples/observation/test-all.sh
```

## Complete walkthroughs

These examples show longer programs with a thin `SKILL.md` and scripted
Expand Down
4 changes: 4 additions & 0 deletions docs/reference/run-receipts.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ profile, and verifies the embedded SHA-256 receipt digest. The SDKs share one
Go-generated golden fixture. They do not read journals, project receipts,
write receipt storage, or participate in replay.

The [four-language reader examples](../../examples/observation/) accept one
canonical receipt-object file and emit the same privacy-safe summary in Go,
TypeScript, Python, and Rust.

## Privacy boundary

Receipts do not contain prompts, instructions, model responses, user answers,
Expand Down
44 changes: 44 additions & 0 deletions examples/observation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# RunReceipt readers

These standalone examples read and verify one already-materialized
`yield.observation.v1` receipt in Go, TypeScript, Python, and Rust. They are
post-run consumers, not skill workflows: the Go supervisor projects the
receipt after a foreground stopping point, and the language readers never read
the journal or participate in replay.

Pass the exact bytes of a content-addressed receipt object. The input must be
canonical JSON with no transport newline or other trailing data.

```bash
RECEIPT=/path/to/.yield/receipts/objects/sha256/ab/abcdef.json

go run ./examples/observation/go "$RECEIPT"
node examples/observation/typescript/main.ts "$RECEIPT"
PYTHONDONTWRITEBYTECODE=1 python3 examples/observation/python/main.py "$RECEIPT"
cargo run --quiet --manifest-path examples/observation/rust/Cargo.toml -- "$RECEIPT"
```

Every example verifies the closed schema, canonical encoding, and embedded
SHA-256 digest before writing output. They emit the same privacy-safe subset:
the schema and receipt digest, run and skill identity, lifecycle phase,
terminal disposition, and operation summaries. Invalid input produces no
partial summary.

The repository examples import the adjacent SDK sources so they always test
the current checkout. In an installed application, use the public packages:

- Go: `github.com/operatorstack/yield/observation`
- TypeScript: `@operatorstack/yield`
- Python: `yieldskill`
- Rust: `yieldskill`

Run all four readers, including strict rejection cases, from the repository
root:

```bash
bash ./examples/observation/test-all.sh
```

The test harness removes the single JSONL framing newline from the shared
golden fixture before invoking the readers. The readers themselves never
normalize receipt bytes.
55 changes: 55 additions & 0 deletions examples/observation/go/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"encoding/json"
"fmt"
"os"

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

type receiptSummary struct {
Schema string `json:"schema"`
ReceiptDigest string `json:"receipt_digest"`
RunID string `json:"run_id"`
Skill string `json:"skill"`
Phase string `json:"phase"`
TerminalDisposition *string `json:"terminal_disposition"`
OperationSummaries []observation.OperationSummary `json:"operation_summaries"`
}

func main() {
if len(os.Args) != 2 {
fail(fmt.Errorf("usage: go run ./examples/observation/go <canonical-receipt.json>"))
}
raw, err := os.ReadFile(os.Args[1])
if err != nil {
fail(err)
}
receipt, err := observation.Parse(raw)
if err != nil {
fail(err)
}

var disposition *string
if receipt.Outcome.TerminalDisposition != "" {
disposition = &receipt.Outcome.TerminalDisposition
}
summary := receiptSummary{
Schema: receipt.Schema,
ReceiptDigest: receipt.ReceiptDigest,
RunID: receipt.Run.ID,
Skill: receipt.Skill.Name,
Phase: receipt.Outcome.Phase,
TerminalDisposition: disposition,
OperationSummaries: receipt.OperationSummaries,
}
if err := json.NewEncoder(os.Stdout).Encode(summary); err != nil {
fail(err)
}
}

func fail(err error) {
fmt.Fprintf(os.Stderr, "receipt example: %v\n", err)
os.Exit(1)
}
38 changes: 38 additions & 0 deletions examples/observation/python/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[3] / "sdk" / "python"))

from yieldskill import parse_run_receipt # noqa: E402


def main() -> int:
if len(sys.argv) != 2:
print(
"usage: python3 examples/observation/python/main.py <canonical-receipt.json>",
file=sys.stderr,
)
return 1

try:
receipt = parse_run_receipt(Path(sys.argv[1]).read_bytes())
except (OSError, ValueError) as error:
print(f"receipt example: {error}", file=sys.stderr)
return 1

summary = {
"schema": receipt["schema"],
"receipt_digest": receipt["receipt_digest"],
"run_id": receipt["run"]["id"],
"skill": receipt["skill"]["name"],
"phase": receipt["outcome"]["phase"],
"terminal_disposition": receipt["outcome"].get("terminal_disposition"),
"operation_summaries": receipt["operation_summaries"],
}
print(json.dumps(summary, separators=(",", ":")))
return 0


if __name__ == "__main__":
raise SystemExit(main())
207 changes: 207 additions & 0 deletions examples/observation/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading