# How Shannon Enforces Agent Prerequisites for Deterministic Execution Order in session-manager

> Learn how Shannon enforces agent prerequisites for deterministic execution order. Discover static dependency maps, topological sorting, and runtime validation in Temporal workflows.

- Repository: [KeygraphHQ/shannon](https://github.com/keygraphhq/shannon)
- Tags: internals
- Published: 2026-02-16

---

**Shannon enforces agent prerequisites through a static dependency map in [`session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/session-manager.ts) that defines prerequisite chains, a topologically sorted `AGENT_ORDER` array, and runtime validation in Temporal workflows that blocks execution until all declared dependencies complete.**

Shannon is an open-source security automation framework developed by KeygraphHQ that orchestrates complex vulnerability assessment pipelines. The [`session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/session-manager.ts) module serves as the central authority for agent metadata, defining which reconnaissance and exploitation agents must complete before others can begin, ensuring deterministic execution even when running independent agents in parallel.

## Understanding Agent Prerequisites in Shannon

### The AgentDefinition Interface

Every agent in Shannon is described by an `AgentDefinition` object that declares its runtime requirements. Located in [`src/session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/session-manager.ts) at lines 10-15, this interface includes a `prerequisites` array containing `AgentName` values that must be satisfied before the agent executes.

```typescript
interface AgentDefinition {
  name: AgentName;
  description: string;
  prerequisites: AgentName[]; // Agents that must complete first
  // ... additional metadata
}

```

### Static Prerequisite Mapping with AGENTS

The constant `AGENTS` (lines 24-33 and 79-83) enumerates every agent alongside its prerequisite list, creating a compile-time dependency graph. For example, the **recon** agent declares `['pre-recon']` as a prerequisite, while the **report** agent lists all exploitation agents to ensure comprehensive data collection before final generation.

```typescript
const AGENTS: Record<AgentName, AgentDefinition> = {
  'pre-recon': {
    name: 'pre-recon',
    prerequisites: [],
    // ...
  },
  'recon': {
    name: 'recon',
    prerequisites: ['pre-recon'], // Depends on pre-recon completion
    // ...
  },
  'report': {
    name: 'report',
    prerequisites: ['exploit-1', 'exploit-2', 'exploit-3'], // Depends on all exploits
    // ...
  }
};

```

## Enforcing Execution Order in session-manager

### Topological Sorting via AGENT_ORDER

Shannon materializes the dependency graph into a linear execution sequence through the `AGENT_ORDER` array (lines 86-101). This ordered array represents a valid topological sort of the prerequisite relationships, ensuring that every agent appears after its dependencies in the sequence.

```typescript
const AGENT_ORDER: AgentName[] = [
  'pre-recon',
  'recon',      // recon comes after pre-recon
  'vuln-scan',
  'exploit-1',  // exploits can be parallelized but come after recon
  'exploit-2',
  'exploit-3',
  'report'      // report comes last, after all exploits
];

```

### Runtime Prerequisite Validation

The actual enforcement occurs in [`src/temporal/workflows.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/temporal/workflows.ts), where the workflow engine iterates over `AGENT_ORDER` before scheduling agents. Before launching any agent, the engine inspects `AGENTS[agent].prerequisites` and verifies that all listed dependencies exist in the set of already-completed agents. Only when **all** prerequisites are satisfied does the workflow dispatch the activity via [`src/temporal/activities.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/temporal/activities.ts).

```typescript
// Conceptual implementation from Shannon's workflow logic
async function executeAgentWorkflow() {
  const completed = new Set<AgentName>();
  
  for (const agentName of AGENT_ORDER) {
    const agentDef = AGENTS[agentName];
    
    // Enforce prerequisites
    const unmetPrereqs = agentDef.prerequisites.filter(
      prereq => !completed.has(prereq)
    );
    
    if (unmetPrereqs.length > 0) {
      throw new Error(
        `Cannot execute ${agentName}: missing prerequisites ${unmetPrereqs.join(', ')}`
      );
    }
    
    // Execute via Temporal activity
    await runAgentActivity(agentName);
    completed.add(agentName);
  }
}

```

## Parallel Execution of Independent Agents

### Grouping Agents with getParallelGroups

Shannon optimizes pipeline throughput by identifying agents that share no mutual dependencies and can run concurrently. The `getParallelGroups()` function (implemented in [`session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/session-manager.ts)) analyzes the `AGENTS` prerequisite map to partition `AGENT_ORDER` into parallelizable batches.

For example, multiple exploitation agents may depend on the reconnaissance phase completing, but not on each other. The function groups these into a single parallel batch, allowing the Temporal workflow to execute them simultaneously using `Promise.all()`, while still respecting the prerequisite chain that requires recon to finish first.

```typescript
import { getParallelGroups, AGENT_ORDER } from './session-manager';

async function runParallelVulnPhase() {
  const groups = getParallelGroups();
  
  // Execute each group sequentially, but agents within a group in parallel
  for (const group of groups) {
    await Promise.all(
      group.map(agentName => runAgentActivity(agentName))
    );
  }
}

```

## Implementation Example

The following example demonstrates a complete prerequisite-aware scheduler that mirrors Shannon's enforcement logic. This pattern ensures that agent prerequisites are validated before execution while supporting both sequential and parallel execution strategies.

```typescript
import {
  AGENTS,
  AGENT_ORDER,
  getParallelGroups,
  AgentName,
} from './session-manager';

class PrerequisiteEnforcer {
  private completed = new Set<AgentName>();

  async runSequential(): Promise<void> {
    for (const agent of AGENT_ORDER) {
      await this.validateAndRun(agent);
    }
  }

  async runParallelGroups(): Promise<void> {
    const groups = getParallelGroups();
    
    for (const group of groups) {
      // Validate all agents in group before parallel execution
      for (const agent of group) {
        this.checkPrerequisites(agent);
      }
      
      // Execute in parallel
      await Promise.all(group.map(agent => this.runAgent(agent)));
      
      // Mark all as completed
      group.forEach(agent => this.completed.add(agent));
    }
  }

  private async validateAndRun(agent: AgentName): Promise<void> {
    this.checkPrerequisites(agent);
    await this.runAgent(agent);
    this.completed.add(agent);
  }

  private checkPrerequisites(agent: AgentName): void {
    const unmet = AGENTS[agent].prerequisites.filter(
      p => !this.completed.has(p)
    );
    
    if (unmet.length > 0) {
      throw new Error(
        `Prerequisite violation: ${agent} requires ${unmet.join(', ')}`
      );
    }
  }

  private async runAgent(agent: AgentName): Promise<void> {
    // Integration with Temporal activities or direct execution
    console.log(`Executing: ${agent}`);
  }
}

```

## Summary

- **Static dependency mapping**: The `AGENTS` constant in [`src/session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/session-manager.ts) defines every agent's prerequisites at compile time, creating a deterministic dependency graph.
- **Topological ordering**: `AGENT_ORDER` provides a linearized sequence that respects all prerequisite chains, ensuring dependent agents always appear after their requirements.
- **Runtime validation**: The Temporal workflow in [`src/temporal/workflows.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/temporal/workflows.ts) enforces prerequisites by checking `AGENTS[agent].prerequisites` against completed agents before dispatching activities.
- **Parallel optimization**: `getParallelGroups()` identifies independent agents that share no mutual dependencies, allowing concurrent execution while maintaining prerequisite integrity.

## Frequently Asked Questions

### What is the role of the AGENTS constant in session-manager.ts?

The `AGENTS` constant serves as the central registry for all agent metadata in Shannon. Defined at lines 24-33 and 79-83 of [`src/session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/session-manager.ts), it maps each `AgentName` to an `AgentDefinition` object containing the `prerequisites` array. This static mapping provides the single source of truth that both the execution order calculator and runtime workflow use to determine dependency relationships.

### How does Shannon handle circular dependencies between agents?

Shannon prevents circular dependencies through its `AGENT_ORDER` array, which represents a valid topological sort of the prerequisite graph. Because `AGENT_ORDER` is statically defined at lines 86-101 of [`session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/session-manager.ts) and manually ordered, any circular dependency would be immediately apparent during development or code review. The linear sequence ensures that an agent always appears after its prerequisites, making circular references impossible to satisfy in the execution plan.

### Can agents run in parallel if they share the same prerequisites?

Yes, agents that share prerequisites but have no mutual dependencies can execute in parallel. The `getParallelGroups()` function in [`session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/session-manager.ts) analyzes the `AGENTS` prerequisite map to partition `AGENT_ORDER` into batches where agents within each batch are independent. For example, multiple exploitation agents may both depend on the reconnaissance phase completing, but not on each other, allowing the Temporal workflow to execute them simultaneously using `Promise.all()`.

### Where is the prerequisite check implemented at runtime?

The runtime prerequisite validation occurs in [`src/temporal/workflows.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/temporal/workflows.ts), where the workflow engine orchestrates the agent pipeline. Before dispatching any agent activity via [`src/temporal/activities.ts`](https://github.com/KeygraphHQ/shannon/blob/main/src/temporal/activities.ts), the workflow checks `AGENTS[agent].prerequisites` against a set of already-completed agents. The engine only proceeds when all listed prerequisites are satisfied, throwing an error if any dependency remains unmet. This enforcement guarantees that the static dependency graph defined in [`session-manager.ts`](https://github.com/KeygraphHQ/shannon/blob/main/session-manager.ts) is respected during actual execution.