Lab · Composing a Sandcastle request
The runnable version of this lab lives at labs/compose-sandcastle-request/. It executes the full compose → construct path end-to-end (Crossplane XR → EngineeringTask → Sandcastle branch → recomposed Pico verification, sandbox disposed) and ships an automatable verify.sh. Start there when you are ready to run the lab.
What you will run through
You will take a learner-authored XHelloWorldPico XR, have a small local simulation of a Crossplane Composition emit an EngineeringTask from it, and have a Sandcastle pick up that task and construct the requested rules/hello.yaml on a dedicated branch. The greeting the branch carries comes from the XR, not from a hard-coded string in the agent.
The hand-off is deliberately narrow: three files cross the boundary in either direction — one XR, one EngineeringTask, one durable branch — and the sandbox is disposed after the push. No cluster is required.
Step 1 · Inspect the Crossplane request
The lab’s only compose-side input is the shipped XR:
cat labs/compose-sandcastle-request/downloads/xr-request.yamlYou should see an XHelloWorldPico with metadata.name: greetings-de and spec.value: "Hallo, Pico!" — the same apiVersion and kind used by the Hello Pico on Kubernetes lab’s 05-xr.yaml, with a deliberately different greeting so you can see the Sandcastle honour the request.
Rule from the lesson being exercised: compose emits declaration only — the XR is the learner’s declaration of what should exist; no Pico code is written by hand.
Step 2 · Compose the EngineeringTask
Run the simulated composition against the XR:
bash labs/compose-sandcastle-request/downloads/compose.sh \
labs/compose-sandcastle-request/downloads/xr-request.yaml \
work/compose-sandcastle-requestInspect the emitted task:
cat work/compose-sandcastle-request/task/engineering-task.yamlIt is an EngineeringTask carrying the greeting under spec.inputs.greeting and the target branch under spec.target.branch. It also carries spec.requestedBy as a provenance pointer back to the XR — no code has been written and no repository has been touched.
Rule being exercised: compose emits declaration only — you can grep compose.sh for pico, git commit, or write file; you will find none.
Step 3 · Run the Sandcastle against the EngineeringTask
Run the driver against the task. It provisions the isolated workspace, copies the task file in as the only compose-side input, runs the agent under a PATH allowlist, and disposes of the workspace after the push:
bash labs/compose-sandcastle-request/downloads/sandcastle-run.sh \
work/compose-sandcastle-request/task/engineering-task.yaml \
work/compose-sandcastle-requestThe [agent] ... lines come from sandcastle-agent.sh running inside the sandbox; the sandcastle-run: ... lines come from the driver running outside the sandbox. Look for greeting requested by task = Hallo, Pico! — the agent reads it from the task file, not from the XR.
Rules being exercised: construct sees only the task — the driver mounts only the task inside the sandbox — and parametric on the request — the greeting on the emitted rule matches the XR’s spec.value, byte for byte.
Step 4 · Confirm the durable branch matches the request
After the driver returns, the sandbox is gone. Only the target repo and the captured evidence remain:
test ! -d work/compose-sandcastle-request/sandbox && echo "sandbox: disposed"
git -C work/compose-sandcastle-request/target-repo \
show sandcastle/hello-world-pico-greetings-de:rules/hello.yaml
cat work/compose-sandcastle-request/results/pico-output.txtThe rule’s value: field and the recomposed Pico’s output both match Hallo, Pico! — proof the construction stage honoured the request end-to-end.
Rule being exercised: artifact boundary intact — only the branch survived; the sandbox, the task file inside it, and the agent’s scratch state were disposed.
Step 5 · Run the automatable verification
Re-run the full path under the packaged verifier:
bash labs/compose-sandcastle-request/downloads/verify.shverify.sh runs compose.sh and sandcastle-run.sh and asserts the five boundary invariants from labs/compose-sandcastle-request/walkthrough.qmd:
- The emitted task matches the reference shape on the boundary fields.
- The branch on the target repo carries the greeting the XR asked for.
- Recomposing from a fresh clone of the branch prints the same greeting.
- The sandbox workspace is gone.
- The construction stage never saw the XR.
Empty diff, exit code 0, and a final verify: OK — branch=... line are the pass.
Success criteria (runnable)
Next
Continue with the Summary.