Exercise · Hands

A short, guided task. Complete it before moving on to the lab. All snippets use provider-neutral capabilities; no external accounts, tokens, or network calls are required.

See the Open Engineering Language Server (OELS) onboarding guide for the local install and editor setup.

TipOELS in your editor

While you work through this content, keep the Open Engineering Language Server (OELS) active in VS Code (or any LSP-capable editor). See the OELS onboarding guide linked at the top of this page (also linked from the academy Resources page) for the one-time install and configuration steps.

What OELS validates. OELS only recognizes OE-shaped YAML/JSON: files with a mapping root that declare an apiVersion beginning with open-engineering.io/, a non-empty kind, and a DNS-1123 metadata.name. Ordinary academy metadata.yaml, Quarto/QMD pages and front matter, Kubernetes/Crossplane/runtime manifests, JSON payload samples, and shell scripts are intentionally non-OE — OELS ignores them silently and you should not add synthetic apiVersion/kind headers to force recognition.

Where you see diagnostics. OELS surfaces UnknownProperty, MissingRequiredProperty, IncorrectType, InvalidEnumValue, MalformedIdentifier, UnknownDefinition, and reference-shape hints in the editor Problems panel as you type. These are independent of quarto render: OELS diagnostics do not block rendering, and Quarto errors do not appear in the Problems panel.

The executable check still comes from this exercise. OELS speeds up editing, but the exercise’s own parser, verifier, or verify.sh (for example the pico parse … command shown below) remains the authoritative success check. Run it as usual.

Task

For a Pico you have been modeling across Part 3, write a capability-based Hands declaration:

# hands-capabilities.yaml
hands:
  - name: external
    capabilities:
      - github.issue.read
      - github.issue.create
  - name: cluster
    capabilities:
      - kubernetes.resource.read

Then write rules that govern the capabilities. Rules name capabilities, not provider tools, and include an actor so authorization is identity-aware:

# hands-rules.yaml
actor:
  pico: pico-alice
rules:
  - capability: github.issue.create
    effect: allow
  - capability: kubernetes.resource.delete
    effect: deny

Finally, sketch the evidence a Pico would keep after a single github.issue.create invocation:

# evidence-sample.yaml
evidence:
  actor: pico-alice
  capability: github.issue.create
  authorized-by: rule-1
  hand: external
  outcome: succeeded
  timestamp: 2026-01-01T00:00:00Z

Answer the reflection questions below.

Note

There is no single correct answer. The goal is to internalize the capability-over-provider model: rules name capabilities, not provider tool names, so the provider can be swapped without changing Pico behaviour.

Success criteria

Automatable check

Save the snippet under a scratch working directory and confirm it is well-formed YAML with the expected capability-based shape. The Hands declaration is a governance artefact, not a Hello Pico Rule, so the check uses python3 rather than pico parse (which only accepts the flat Rule shape taught in Part 1). No external service is contacted.

mkdir -p scratch-hands
cat > scratch-hands/hands.yaml <<'EOF'
hands:
  - name: external
    capabilities:
      - github.issue.read
      - github.issue.create
rules:
  - capability: github.issue.create
    effect: allow
  - capability: kubernetes.resource.delete
    effect: deny
EOF
python3 - <<'PY'
import sys, yaml
doc = yaml.safe_load(open("scratch-hands/hands.yaml"))
hands = doc.get("hands") or []
rules = doc.get("rules") or []
ok = (
    isinstance(hands, list) and hands
    and all(h.get("name") and h.get("capabilities") for h in hands)
    and isinstance(rules, list) and rules
    and all(r.get("capability") and r.get("effect") in {"allow", "deny"} for r in rules)
)
print("OK: Hands declaration parses" if ok else "FAIL")
sys.exit(0 if ok else 1)
PY

The command exits with status 0 and prints an OK: line when the declaration is well-formed.

Reflect

  • Why would a rule say email.send rather than GMAIL_SEND_EMAIL?
  • What does “a valid credential must not imply authorization” mean when Composer equipment and Open Engineering policy are separate concerns?
  • Why is the evidence record kept even when the outcome is succeeded?

Next

Continue with the Lab.