Task Block Library

Task blocks are the reusable processing units of Foundry. Each block is defined once and can participate in multiple workflows.

Implementing the TaskBlock Trait

Every task block implements foundry_core::task_block::TaskBlock:

#![allow(unused)]
fn main() {
pub trait TaskBlock: Send + Sync {
    fn name(&self) -> &'static str;
    fn kind(&self) -> BlockKind;
    fn sinks_on(&self) -> &[EventType];
    fn execute(
        &self,
        trigger: &Event,
    ) -> Pin<Box<dyn Future<Output = anyhow::Result<TaskBlockResult>> + Send + '_>>;

    // Optional — defaults to no retries
    fn retry_policy(&self) -> RetryPolicy {
        RetryPolicy::default()
    }
}
}

The trait provides default implementations for should_emit() and should_execute() based on kind() and the throttle level. Override retry_policy() to enable automatic retry of transient failures.

See the Writing Task Blocks guide for step-by-step instructions and a full example including RetryPolicy.

Current Blocks

Hello-World (validates engine mechanics)

BlockKindSinks OnEmits
Compose GreetingObservergreet_requestedgreeting_composed
Deliver GreetingMutatorgreeting_composedgreeting_delivered

Vulnerability Remediation

These blocks form two paths through the vulnerability remediation workflow. Both Remediate Vulnerability and Cut Release sink on main_branch_audited and self-filter based on the dirty flag in the payload — only one path fires per event.

Audit Release Tag also sinks on project_changes_pushed to perform a post-push re-audit, confirming the fix is clean before anything downstream acts.

BlockKindSinks OnEmitsSelf-filters
Scan DependenciesObserverscan_requestedvulnerability_detected
Audit Release TagObservervulnerability_detected, project_changes_pushedrelease_tag_auditedSkips post-push when project not in registry
Audit Main BranchObserverrelease_tag_auditedmain_branch_auditedSkips when vulnerable=false
Remediate VulnerabilityMutatormain_branch_auditedremediation_completedOnly when dirty=true
Commit and PushMutatorremediation_completed, project_iteration_completed, project_maintenance_completedproject_changes_committed, project_changes_pushedSkips when tree is clean or changes=false
Cut ReleaseMutatormain_branch_auditedrelease_completedOnly when dirty=false
Watch PipelineMutatorrelease_completedrelease_pipeline_completed
Install LocallyMutatorproject_changes_pushed, release_pipeline_completedlocal_install_completed

Maintenance

The maintenance workflow uses an explicit routing Observer (Route Project Workflow) to delineate which sub-workflow runs. This keeps each downstream block focused on a single responsibility.

BlockKindSinks OnEmitsSelf-filters
Validate ProjectObservermaintenance_run_startedproject_validation_completedSkips projects not in active registry
Route Project WorkflowObserverproject_validation_completediteration_requested or maintenance_requestedStops when status != "ok" or no actions enabled
Commit and PushMutatorproject_iteration_completed, project_maintenance_completedproject_changes_committed, project_changes_pushedSkips when tree is clean
Audit Release TagObserverproject_changes_pushedrelease_tag_auditedSkips when project not in registry

The actions.maintain flag is forwarded inside the iteration_requested payload so that the gate routing can chain directly to maintenance_requested after a successful iteration without re-querying the project configuration.

Gate Orchestration

These blocks provide native gate resolution, execution, and routing for iterate, maintain, and validation workflows.

BlockKindSinks OnEmitsSelf-filters
Resolve GatesObserveriteration_requested, maintenance_requested, validation_requestedgate_resolution_completed
Run Preflight GatesObservergate_resolution_completedpreflight_completedSkips maintain workflow
Run Verify GatesObserverexecution_completedgate_verification_completed
Route Gate ResultObservergate_verification_completedproject_iteration_completed / project_maintenance_completed / retry_requestedRoutes based on pass/fail and retry count
Route Validation ResultObserverpreflight_completedvalidation_completedOnly handles validate workflow

Iterate Workflow

These blocks form the native iterate chain, running inside the gate orchestration lifecycle.

BlockKindSinks OnEmitsSelf-filters
Check CharterObserverpreflight_completedcharter_validatedOnly handles iterate workflow
Assess ProjectMutatorcharter_validatedproject_assessed
Triage AssessmentMutatorproject_assessedassessment_triaged
Create PlanMutatorassessment_triagedplan_created
Execute PlanMutatorplan_createdexecution_completed

Maintain Workflow

BlockKindSinks OnEmitsSelf-filters
Execute MaintainMutatorgate_resolution_completedexecution_completedOnly handles maintain workflow
Retry ExecutionMutatorretry_requestedexecution_completed
Summarize ResultObserverproject_iteration_completed, project_maintenance_completedgenerate_summary

Validation Workflow

A dedicated read-only workflow for checking project gate health. No Mutator blocks are involved — validation never modifies code.

validation_requested
  → Resolve Gates → gate_resolution_completed
    → Run Preflight Gates → preflight_completed
      → Route Validation Result → validation_completed

Vulnerability Workflow Chain

flowchart TD
    A[scan_requested] --> B([Scan Dependencies])
    B --> C[vulnerability_detected]
    C --> D([Audit Release Tag])
    D --> E[release_tag_audited]
    E --> F([Audit Main Branch])
    F --> G[main_branch_audited]
    G --> H{dirty?}
    H -->|dirty=true| I([Remediate Vulnerability])
    I --> J[remediation_completed]
    J --> K([Commit and Push])
    K --> L[project_changes_committed]
    K --> M[project_changes_pushed]
    M --> N([Audit Release Tag post-push])
    N --> O[release_tag_audited]
    M --> P([Install Locally])
    P --> Q[local_install_completed]
    H -->|dirty=false| R([Cut Release])
    R --> S[release_completed]
    S --> T([Watch Pipeline])
    T --> U[release_pipeline_completed]
    U --> V([Install Locally])
    V --> W[local_install_completed]

Maintenance Workflow Chain

flowchart TD
    A([maintenance_run_started]) --> B[[Validate Project]]
    B --> C([project_validation_completed])
    C --> D[[Route Project Workflow]]
    D -->|iterate=true| E([iteration_requested])
    D -->|iterate=false, maintain=true| F([maintenance_requested])
    D -->|no actions| G([end — no automation])
    E --> H[[Resolve Gates]]
    H --> I[[Run Preflight Gates]]
    I --> J[[Check Charter]]
    J --> K[[Assess Project]]
    K --> L[[Triage Assessment]]
    L --> M[[Create Plan]]
    M --> N[[Execute Plan]]
    N --> O[[Run Verify Gates]]
    O --> P[[Route Gate Result]]
    P -->|pass| Q([project_iteration_completed])
    P -->|fail, retries left| R[[Retry Execution]]
    Q -->|maintain=true| F
    F --> S[[Resolve Gates]]
    S --> T[[Execute Maintain]]
    T --> U[[Run Verify Gates]]
    U --> V[[Route Gate Result]]
    V -->|pass| W([project_maintenance_completed])
    V -->|fail, retries left| X[[Retry Execution]]

Gateway Pattern

Every block that executes an external process (a shell command or an audit tool) receives its I/O capability through a gateway trait rather than calling the implementation module directly. Two gateway traits live in gateway.rs:

  • ShellGateway — wraps crate::shell::run for arbitrary command execution.
  • ScannerGateway — wraps crate::scanner::run_audit for vulnerability scanning.

Production blocks hold an Arc<dyn ShellGateway> (and/or Arc<dyn ScannerGateway>) which is initialised to the real implementation in new(). A #[cfg(test)] constructor accepts a fake instead, enabling hermetic unit tests for every block.

This separation means:

  • The happy path and every failure/edge-case branch can be tested without spawning real processes.
  • shell.rs and scanner.rs stay untouched; the gateway is a thin adapter.
  • No async_trait macro is required — the return type uses an explicit Pin<Box<dyn Future + Send + '_>> (the same pattern as TaskBlock::execute).

See Testing with Fakes for usage examples.

RetryPolicy

Blocks can declare automatic retry behaviour by overriding retry_policy():

#![allow(unused)]
fn main() {
use std::time::Duration;
use foundry_core::task_block::RetryPolicy;

fn retry_policy(&self) -> RetryPolicy {
    RetryPolicy {
        max_retries: 3,
        backoff: Duration::from_secs(5),
    }
}
}

max_retries: 0 (the default) means the block runs exactly once. With max_retries: N, the engine retries up to N times after any failure (either a returned Err or a TaskBlockResult { success: false, .. }), sleeping backoff between each attempt. The final result (success or failure) is what the engine records in the BlockExecution trace.