Skip to content

You are viewing documentation for Instruqt 2.0 Labs which is in Beta currently. Official release date - 29 September, 2026. For Tracks documentation, please visit docs.instruqt.com.

Interactive Tasks


Tasks are the hands-on heart of an Instruqt lab. A task asks the user to do real technical work in the sandbox – create a file, start a service, merge a branch – and validates that work automatically with scripts you write. When the user clicks Check, your validation scripts run inside the sandbox and the user gets immediate feedback on what passed and what still needs fixing.

Every task has the same three-level structure, whether you build it in the UI or in code:

  • Task – the activity itself. It carries a description shown to the user, an optional success message, and the default execution configuration (which sandbox component to run on, as which user, with what timeout).
  • Conditions – the individual requirements that make up the task, shown to the user as a checklist. Conditions are validated in the order you define them (a failing condition doesn’t stop the later ones from being checked). Each condition has a user-facing description like “A branch named add-q3-goals exists”.
  • Scripts – the executable code attached to each condition. Three types, each with its own moment in the lifecycle:
Script type When it runs
Setup When the task becomes active – all of its conditions unlock together, and their setup scripts run then
Check When the user clicks Check – validate whether the requirement is met
Solve When the user clicks Solve for me – perform the work for them so the lab can continue

A check script signals success by exiting with code 0 and failure with any other exit code. On failure, the user sees the failure message you wrote for that script – this is your main channel for guiding stuck users, so make it actionable: “No branch named add-q3-goals found. Create it with git checkout -b add-q3-goals.”

You embed a task on an instruction page, where it renders as a card showing the task description and the checklist of conditions. The user does the work in the sandbox (terminal, editor, cloud console – whatever your lab provides), then clicks Check. Conditions validate in order; each one gets a status indicator as it passes or fails. If a check fails, the user sees your failure message highlighted under the failing condition and can try again. If they’re stuck, Solve for me runs your solve scripts and marks the task with a “Solved for you” badge. When every condition passes, the task is complete and your success message appears.

Two behaviors shape how users move through a lab:

  • Tasks on a page complete in order. When a page has more than one task, the later ones show a Locked badge until the earlier ones are completed or solved.
  • Activities gate navigation. A page with unfinished tasks or quizzes shows “Complete N activities to continue” and keeps the Next page button disabled until every activity on the page is done. Solve for me counts as done, so users are never stuck on a page. This gating is built in and applies to every lab.

The lab editor gives you the complete task workflow without touching code. In edit mode:

  1. Create the task. Go to the Activities tab, click +, and choose Task. The task editor opens with three areas: task details (name, description, success message), configuration, and the conditions sidebar.
  2. Point it at your sandbox. Under Configuration, set Target to the sandbox component (container or VM) where scripts should run. You can also set the user, group, working directory, timeout, exit codes, and parallel execution here – these become the defaults for every condition in the task.
  3. Add conditions. Click + next to Conditions in the sidebar. Give each condition an internal name and a user-facing description. A condition can override the task-level configuration when it needs, say, a longer timeout.
  4. Add scripts. Inside a condition, click + next to Scripts and pick the type – Setup, Check, or Solve. Choose New to write the script in the inline editor, or Existing to reuse a script file already in your lab. For check scripts, add a failure message. Scripts always run with sh inside the target, regardless of the file name or shebang line – write them as shell scripts, and call other interpreters explicitly if you need them (e.g. python3 - <<'EOF' ... EOF). A .py file will not run as Python on its own.
  5. Save your work. Click Add changes to keep the task in your editing session, or Discard changes to throw the edits away. Like all edits in the lab editor, tasks go live when you publish the lab.
  6. Embed the task in a page. In the Instructions tab, open the page where users should encounter the task and click the Task button in the editor toolbar. Pick an existing task from the list (it shows each task’s condition count) or create a new one on the spot. The editor inserts the component at your cursor:
<instruqt-task id="branch_workflow"></instruqt-task>

The id matches the task’s internal name from the Activities tab. In the page’s code view you’ll see the same tag, so you can also place it by hand. A task can appear only once per page.

For every field and rule in detail – naming constraints, configuration defaults, deleting tasks that are in use – see Creating Tasks.

If you develop your lab locally, tasks are HCL resources. Define them in a tasks.hcl (or any .hcl file in your lab directory) with scripts stored under scripts/:

tasks.hcl
resource "task" "branch_workflow" {
description = "Create a branch, edit the roadmap, and merge it back into main."
success_message = "Nice work – that's the full collaboration loop."
config {
target = resource.container.workstation
timeout = "30s"
}
condition "branch_created" {
description = "A branch named `add-q3-goals` exists"
check {
script = "scripts/branch_workflow/branch_created.sh"
failure_message = "No branch named `add-q3-goals` found. Create it with `git checkout -b add-q3-goals`."
}
solve {
script = "scripts/branch_workflow/solve.sh"
}
}
}

A minimal check script:

scripts/branch_workflow/branch_created.sh
#!/bin/sh
# Pass when a local branch named add-q3-goals exists.
git -C /root/product-roadmap show-ref --verify --quiet refs/heads/add-q3-goals

Reference the task from your instruction markdown with the same <instruqt-task id="..."> component the UI inserts. Validate your configuration locally with instruqt lab validate before pushing – it checks that every referenced script file exists, and rejects inline multi-line scripts (scripts must live in files). See Testing & Validation. Defaults if you don’t configure them: 30s timeout per script, user and group root, working directory /.

The full resource syntax – environment variables, per-script configuration overrides, parallel execution – is in the Task reference.

  • Check state, not history. Verify the outcome (“the file contains X”, “the service responds”) rather than how the user got there. Users take unexpected paths.
  • Exit codes are the contract. 0 means pass; anything in your failure exit codes means fail. Print helpful output – it lands in the lab logs and makes debugging much easier.
  • Write failure messages for a stuck user. Name the thing that’s missing and the next action to take.
  • Make scripts safe to re-run. Users click Check repeatedly; setup and check scripts should behave the same on the fifth run as the first.
  • Keep solve scripts honest. Solve should produce exactly the state your check validates, so a solved condition never fails a later re-check.
  • Write for sh. Scripts run with sh no matter what the file is named – avoid bash-only syntax unless you invoke bash yourself.
  • Organize scripts by task. The convention is scripts/<task_name>/<script>.sh – shared scripts can live anywhere, since both workflows let you reference an existing file.

Tasks only prove themselves at runtime, so playtest them:

  1. Start the lab from the editor with Play (or from your team page).
  2. Walk the happy path: do the work, click Check, confirm every condition passes.
  3. Break things on purpose: click Check before doing the work and confirm the failure messages are helpful, not cryptic.
  4. Click Solve for me on each condition and confirm the lab still works afterward.
  5. If a check misbehaves, read the script output in the lab logs (instruqt lab logs <team>/<lab> or the Logs UI).