Lab 2 — A Multi-Pack Pipeline
Lab 2 — A Multi-Pack Pipeline¶
Time: 30 minutes · Level: intermediate
Target audience¶
Developers who finished Lab 1 and want to compose commands instead of writing glue code.
Prerequisites¶
- Lab 1 completed: developer mode enabled, a publishing namespace, and a published pack. Bump
versionfrom0.0.0before each publish. - A Huitzo API token for calling the REST API (see REST API).
- Basic familiarity with Pydantic models.
Learning objectives¶
By the end of this lab you can:
- Declare a pipeline in
huitzo.yamland explain how stages are chained. - Reference a command published by a different pack, and grant the permission that requires.
- Run the pipeline through the REST API and read the per-stage result.
- Trigger a stage failure at run time and see which stage failed.
1. Plan the shape¶
You will build two packs:
- Provider pack — publishes one command,
clean, that normalises a string. (Reuse your Lab 1 pack if you prefer: add acleancommand to it and publish a new version.) - Pipeline pack — declares a pipeline with three stages:
clean(from the provider pack),shout(a command in the pipeline pack), andframe(a command in the pipeline pack).
Stage n's output keys must structurally match stage n+1's input keys; the executor checks that on every transition. Each stage is a regular @command with typed Pydantic models; see Your First Pack, Step 2 for the command shape.
2. Publish the provider¶
Scaffold and publish the provider exactly as in Lab 1. Note its full identity, @yourscope/provider-pack, and the command's namespace:command name (in the block below, provider is the provider pack's namespace).
3. Declare the pipeline and the grant¶
In the pipeline pack's huitzo.yaml, add a pipelines: block whose first stage uses the cross-pack form @scope/pack:namespace:command, and the two local stages use namespace:command. Then grant the permission the cross-pack stage needs:
# pseudocode — the parts of huitzo.yaml that are new in this lab
permissions:
- "storage:read"
- "commands:execute:@yourscope/provider-pack"
policy:
allowed_actions:
- "storage:read"
- "commands:execute:@yourscope/provider-pack"
pipelines:
shout-and-frame:
timeout: 60
stages:
- name: clean
command: "@yourscope/provider-pack:provider:clean"
- name: shout
command: "labpipe:shout"
- name: frame
command: "labpipe:frame"
Every permission must also appear in policy.allowed_actions, and the @ and full scope are required: there is no de-scoped spelling. A grant only says your pack may call the target; at execution the platform also requires that your tenant may run the target command, so naming another tenant's private pack authorizes nothing.
4. Implement the two local stages¶
Write shout (uppercase the text; raise ValueError if the text is empty, so you can trigger a failure in step 7) and frame (wrap it) as ordinary @command functions with Pydantic argument and result models whose field names line up with the previous stage. Keep bodies non-blocking.
5. Publish and run¶
# pseudocode
huitzo pack validate
huitzo pack publish
validate catches a missing grant for a cross-pack stage as an error, so fix any it reports before publishing. Then run the pipeline:
# pseudocode — REST request (see the REST API reference for auth)
POST /api/v1/pipelines/{scope}/{pack}/{name}/execute
Authorization: Bearer <your token>
{ "initial_args": { "text": " hello pipeline " } }
6. Read the result¶
A successful inline run returns HTTP 200 with the result under data.pipeline_run: status is SUCCESS, stage_results has one entry per stage with its duration, and final_output is a list of chunk envelopes holding the last stage's data. (If you get HTTP 202 with a task_id, the run was dispatched asynchronously; this lab assumes the inline 200 — see the REST API for the async path.)
7. Fail a stage at run time¶
Run the pipeline again with { "initial_args": { "text": "" } }. The shout stage raises, the pipeline stops (fail-fast), and the response is an error rather than a SUCCESS result. Find which stage failed and confirm the frame stage did not run.
8. Break the grant on purpose¶
Remove the commands:execute: line from permissions only (keep at least one other permission listed, such as storage:read, so the list still exists — a manifest with no permissions: list at all produces only a warning), and run huitzo pack validate again. You should see an error naming the stage and the missing permission. Restore the line. This is the install-time half of the cross-pack gate; the runtime half re-checks at execution and also requires that your tenant may run the target command (a private pack belonging to another tenant is denied even when the grant is present).
Completion check¶
You are done when the pipeline run returns status: SUCCESS, has exactly three entries in stage_results, and the first entry's stage name is the cross-pack clean stage. You should also have seen the run-time failure in step 7 and the validation error in step 8.
Follow-on resources¶
- Pack Manifest — the
pipelines:andpermissions:reference - REST API — executing pipelines and fetching a run by id
- Lab 3 — Publish and Share a Pack — next in the path