Skip to content

Handle wiring

Handles are the mechanism that connects steps together. A step declares the resources it produces as outputs, and later steps reference those resources as inputs. The test runner resolves handles at runtime, passing actual resource IDs to your evaluation code.

Handle naming

Handle names follow a strict format: {OO}_{SS}_{resource_name}

PartFormatExample
OOTwo-digit zero-padded objective ID01, 02
SSTwo-digit zero-padded step ID within the objective01, 03
resource_nameLowercase alphanumeric segments joined by underscores, starting with a lettervpc_id, function_arn, bucket_name

Full examples: 01_01_vpc_id, 01_02_igw_id, 02_01_function_arn, 03_01_public_subnet_1_id

Rules

  1. Fully qualified names — output names always include the objective and step prefix. 01_02_bucket, not bucket.
  2. Prefix must match the step — a step with objective 01, step 02 can only declare outputs starting with 01_02_.
  3. Inputs reference prior outputs — a step can only reference handles from earlier steps. Forward dependencies (referencing a handle from a later step) are not allowed.
  4. Cross-objective references are fine — a step in objective 02 can reference handles from objective 01.

Output declarations

Each output needs three fields:

outputs:
- name: 01_01_vpc_id # handle name — must match {OO}_{SS}_{resource_name}
type: AWS::EC2::VPC # CloudFormation resource type, or "string"
description: The VPC that will contain all project resources

The type field must be either a valid CloudFormation resource type (AWS::S3::Bucket, AWS::Lambda::Function, etc.) or the literal "string" for non-resource outputs like ARNs, URLs, and names.

Input declarations

Inputs reference prior outputs by handle name:

inputs:
- 01_01_vpc_id
- 01_02_igw_id

A step with no dependencies omits the inputs field entirely (or uses an empty list).

Worked example — handles flowing across objectives

objectives:
- id: "01"
title: Create VPC Infrastructure
steps:
- id: "01"
title: Create VPC
instructions:
- Create a VPC with CIDR block 10.0.0.0/16
outputs:
- name: 01_01_vpc_id
type: AWS::EC2::VPC
description: The VPC for all project resources
- id: "02"
title: Create public subnets
instructions:
- Create two public subnets in different availability zones
inputs:
- 01_01_vpc_id
outputs:
- name: 01_02_subnet_a_id
type: AWS::EC2::Subnet
description: Public subnet in AZ-a
- name: 01_02_subnet_b_id
type: AWS::EC2::Subnet
description: Public subnet in AZ-b
- id: "02"
title: Deploy the Application
steps:
- id: "01"
title: Create the load balancer
instructions:
- Create an Application Load Balancer in the public subnets
inputs:
- 01_01_vpc_id # from objective 01, step 01
- 01_02_subnet_a_id # from objective 01, step 02
- 01_02_subnet_b_id # from objective 01, step 02
outputs:
- name: 02_01_alb_arn
type: AWS::ElasticLoadBalancingV2::LoadBalancer
description: The application load balancer
- name: 02_01_alb_dns
type: string
description: The DNS name of the load balancer

How handles flow through the test runner

When the test runner executes a step, it:

  1. Runs the solution code, which creates the AWS resources and returns a dict of handle names to resource IDs
  2. Creates generated handles from the solution’s return value — these are unvalidated at this point
  3. Runs the evaluation code, which receives the generated handles alongside any events the resources emitted
  4. Promotes handles to resolved status for each handle the evaluation code includes in its validated list

Evaluation code receives two categories of handles:

  • Qualified handles — the full set of all resolved handles from prior steps, keyed by handle name
  • Unqualified handles — the handles from the current step’s solution output, keyed by just the resource name (without the {OO}_{SS}_ prefix)

This means your evaluation code can reference context["resolved"]["01_01_vpc_id"] for a prior step’s VPC, or context["unqualified"]["vpc_id"] for the current step’s VPC.

Only handles that appear in the evaluation’s validated list are promoted to resolved and made available to subsequent steps. If a handle isn’t validated, downstream steps won’t receive it.

For the full evaluation code contract, see Evaluation code.