# How Workflow Commands Are Managed in the AI Job Search Framework

> Discover how workflow commands are managed in the AI Job Search framework. Explore declarative Markdown files for runtime interpretation, avoiding recompilation.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: internals
- Published: 2026-08-30

---

**Workflow commands in the AI Job Search framework are defined declaratively in Markdown specification files under `.claude/commands/`, rather than implemented as hard-coded logic, enabling runtime interpretation without recompilation.**

The MadsLorentzen/ai-job-search repository employs a unique **declarative command architecture** where every user-facing workflow—such as `/apply`, `/scrape`, and `/rank`—is fully specified in a human-readable Markdown file. This design separates workflow orchestration from implementation, making the system auditable, extensible, and portable across multiple agent runtimes.

## Command Definitions in `.claude/commands/`

Each workflow command resides as a standalone Markdown file in the hidden directory **`.claude/commands/`**. These files serve as the **single source of truth** for command behavior.

A command specification includes:

- **Input contract** — what arguments the command accepts
- **Step-by-step workflow** — which skill modules are invoked and how data flows between steps
- **Security rules** — Bash actions permitted via the project-wide allowlist
- **Output contract** — generated files (CV, cover letter, PDF) and their delivery mechanism

The top-level **[`AGENTS.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/AGENTS.md)** file directs every agent runtime to these canonical specifications, implementing a thin-pointer design that keeps runtimes synchronized without code duplication.

## Skill Module Invocation

Actual work is performed by **skill modules** located in `.claude/skills/` and `.agents/skills/`. Command files reference these skills by name, ensuring workflow definitions remain agnostic to concrete implementations.

This decoupling allows skills to evolve independently—for example, the `job-application-assistant` skill could be rewritten without modifying the `/apply` command specification that invokes it.

## Security Enforcement Through Allowlists

The framework enforces strict security boundaries via **[`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json)**, which enumerates exactly which Bash commands a workflow may execute. Examples include `lualatex` for PDF compilation.

The CI job **`security-guards`** validates that no command file attempts to widen this allowlist. This guarantees that workflows cannot execute unsafe shell code, even if a command specification is compromised.

## Runtime Execution Flow

When a user runs a command via CLI:

1. The CLI reads the corresponding Markdown spec (e.g., [`.claude/commands/apply.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md))
2. Arguments are interpolated into the specification
3. Steps are dynamically executed, invoking referenced skills
4. Output files are generated and presented to the user

This architecture means **entire workflows can be updated by editing Markdown**—no recompilation or redeployment required.

### Example: Running the `/apply` Command

```bash

# Execute the full application workflow for a job posting

ai-job-search /apply https://jobindex.dk/job/12345

```

### Example: Command Specification Structure

```markdown
<!-- .claude/commands/apply.md (excerpt) -->

# /apply command

## Arguments

- $ARGUMENTS: job posting URL or raw text

## Workflow

1. Store the posting verbatim (untrusted data rule)
2. Invoke the `job-application-assistant` skill for fit evaluation
3. Generate CV & cover letter using the active LaTeX template
4. Run the verification checklist from `CLAUDE.md`
5. Compile PDFs with `<CV_COMPILE>` / `<COVER_COMPILE>` (allowed by settings.json)
6. Present final output to the user

```

### Simplified Execution Glue

```python
def run_apply(url: str):
    posting = fetch_posting(url)                          # step 1

    evaluation = invoke_skill('job-application-assistant', posting)  # step 2

    cv_pdf, cover_pdf = render_documents(evaluation)      # steps 3-5

    verify_and_present(cv_pdf, cover_pdf)                 # step 6

```

## Core Command Files and Their Roles

| File | Purpose |
|------|---------|
| [`.claude/commands/apply.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/apply.md) | Defines the `/apply` workflow for drafting and submitting applications |
| [`.claude/commands/rank.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/rank.md) | Orchestrates `/rank` for scoring scraped postings before handoff to `/apply` |
| [`.claude/commands/setup.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/commands/setup.md) | Initializes candidate profile data for subsequent commands |
| [`AGENTS.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/AGENTS.md) | Thin-pointer file directing all agent runtimes to canonical specifications |
| [`README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/README.md) | High-level overview of available workflow commands |
| [`SECURITY.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SECURITY.md) | Documents permission model and safety guarantees |
| [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) | Machine-readable allowlist of permitted Bash commands |

## Summary

- **Workflow commands are Markdown-first** — behavior is declared in `.claude/commands/` files, not encoded in source code
- **Single source of truth** — [`AGENTS.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/AGENTS.md) and command specs keep multiple runtimes synchronized
- **Security by design** — [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) allowlist plus `security-guards` CI prevents unsafe execution
- **Zero-downtime updates** — edit command specs without recompilation or redeployment
- **Clean separation** — workflow orchestration (commands) is decoupled from implementation (skills)

## Frequently Asked Questions

### How does the AI Job Search framework prevent malicious code execution in workflow commands?

The framework implements **defense in depth**: [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) maintains an explicit allowlist of permitted Bash commands, the `security-guards` CI job validates that command files never expand this list, and [`SECURITY.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/SECURITY.md) documents the permission model. Commands can only invoke shell tools that have been pre-approved.

### Can I add a new workflow command without modifying Python code?

Yes. Create a new Markdown file in `.claude/commands/` following the established specification format, define your inputs/outputs, reference existing or new skills, and ensure any required Bash commands are added to [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) with proper security review. The CLI will automatically recognize and execute the new command.

### What happens if a skill implementation changes—do command files break?

No. Command files reference skills by **stable names**, not by implementation details. As long as the skill maintains its interface contract, the underlying implementation can be refactored, upgraded, or replaced without touching command specifications.

### Where is the authoritative list of available workflow commands?

The **[`AGENTS.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/AGENTS.md)** file at repository root serves as the canonical index, pointing all agent runtimes to the command specifications in `.claude/commands/`. For human-readable documentation, see [`README.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/README.md).