Exercise · Trace the compose → construct hand-off on paper

A short, pen-and-paper task. Complete it before moving on to the runnable Lab.

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

Trace, on paper, the two-file hand-off from a Crossplane XR to a Sandcastle-produced branch, then use the trace to spot two boundary violations this lesson intentionally rules out.

You do not need a cluster or the runnable lab for this exercise. The goal is to lock in the compose → construct boundary as a one-page picture before touching any tooling.

Step 1 · List the boundary inputs and outputs

Copy this table into your notes and fill in the blanks:

Position File on disk Field
Crossplane input xr-request.yaml spec.___
Composition output engineering-task.yaml spec.inputs.___
Sandcastle input engineering-task.yaml spec.inputs.___
Sandcastle output rules/hello.yaml on branch ___________ ___

Answer in your notes: how many fields carry the greeting all the way from the XR to the branch? (Answer: one — the greeting value, under three different names on the three files.)

Step 2 · Diagram the hand-off

Sketch a three-arrow diagram:

   [xr-request.yaml]                             (compose side)
   apiVersion: oe.academy/v1alpha1
   kind: XHelloWorldPico
   spec.value: "Hallo, Pico!"
       │
       │ (1) Composition: read XR, emit EngineeringTask
       ▼
   [engineering-task.yaml]                       (boundary artifact)
   kind: EngineeringTask
   spec.inputs.greeting: "Hallo, Pico!"
   spec.target.branch:   sandcastle/hello-world-pico-greetings-de
       │
       │ (2) Sandcastle: read task, run agent inner loop
       ▼
   [rules/hello.yaml on target branch]           (construct side)
   value: "Hallo, Pico!"
       │
       │ (3) Sandcastle: push branch back to durable target repo,
       │     dispose of sandbox
       ▼
   [durable branch on target-repo, sandbox gone]

Answer in your notes: what would you need to change on either side of the diagram if the greeting were different? (Answer: only spec.value in the XR; the two downstream files inherit it.)

Step 3 · Spot the boundary violations

For each of the following hypothetical implementations, mark ✅ if it is well-behaved or ❌ if it violates one of the four rules from the lesson. For each ❌, name which rule.

Case 1

The Composition script (compose.sh) not only emits an EngineeringTask, it also opens the target repository and writes rules/hello.yaml directly — “to save the Sandcastle a step”.

❌ Violates compose emits declaration only — Crossplane’s job is to request engineering work; writing Pico code is the Sandcastle’s job. Doing it in compose.sh collapses the compose → construct boundary.

Case 2

The driver passes both the EngineeringTask and the original XR into the sandbox, and the agent reads spec.value directly from the XR.

❌ Violates construct sees only the task — the Sandcastle should be independent of the XR shape. If the composition later starts translating a different XR kind into the same task shape, the agent would have to be rewritten.

Case 3

The agent script hard-codes value: "Hello, Pico!" and ignores the greeting in the EngineeringTask, “since that is what the tests expect”.

❌ Violates parametric on the request — the request would be a lie. Two different XRs would silently produce the same branch, and a reviewer looking at spec.requestedBy would be misled.

Case 4

The Composition writes only the EngineeringTask, the driver passes only the task into the sandbox, the agent reads spec.inputs.greeting from the task and writes the requested value into rules/hello.yaml, and the sandbox is disposed after the push.

✅ Well-behaved — this is exactly what compose.sh, sandcastle-run.sh, and sandcastle-agent.sh implement together.

Case 5

The EngineeringTask omits spec.requestedBy “because the branch will be reviewed anyway”.

❌ Violates provenance retained but disposable — the branch on its own does not tell you which XR requested it. Provenance is cheap to include and expensive to reconstruct.

Success criteria (paper)

Next

Run the hand-off end-to-end in the Lab.