Part III Scale It From Loop to System

Chapter 7 – Intent as Code (Mission Objects)

The dream of a fully autonomous engineering system often gets bogged down in the reality of instruction tweaking. We’ve all been there: a meticulously crafted instruction, revised endlessly, still produces inconsistent results, or worse, confidently wrong output. The core problem is that a raw instruction string is not a contract. It lacks explicit scope, verifiable gates, and a clear path for recovery when things go sideways.

This chapter introduces the Mission Object: your typed, executable contract for what a GenAI system should achieve. It’s not just a longer instruction; it’s a structured declaration of intent that brings rigor and reliability to your AI-driven engineering tasks.

Terminology note: a mission is the abstract objective (“update the docs”). A Mission Object is the versioned YAML/JSON artifact that encodes that objective plus scope, budgets, gates, and fallbacks.

From Ad-hoc Instructions to Typed Mission Objects

Imagine you want an AI to update a specific section of your README.md to reflect a new API endpoint. With a traditional approach, you might write: “Update the ‘API Endpoints’ section in README.md to include /v2/users and describe its pagination.”

That instruction is ambiguous:

A Mission Object transforms this opaque instruction into a clear, executable specification. It’s a structured data format (often YAML or JSON) that codifies the goal, scope, constraints, quality gate, fallbacks, and telemetry for a task.

Here’s how a Mission Object might look for our README update task:


# Example
mission_id: update-readme-api-endpoint-v1
goal: Ensure README.md documents the /v2/users API endpoint
scope:
  target_files:

    - README.md
  target_sections:

    - "API Endpoints" # Restrict AI to modifying content within this heading
constraints:

  - path: "README.md"
    must_contain_strings:

      - "/v2/users"

      - "pagination"
    must_not_contain_strings:

      - "/v1/users"

  - path: "README.md"
    regex_match:

      - pattern: "- `/v2/users`: Supports pagination and user resource management."
        description: "Exact line for new endpoint description."
quality_gate:
  validator_script: ./scripts/validate_readme.sh
  expected_output_schema:
    type: object
    properties:
      new_endpoint_present:
        type: boolean
        description: "True if /v2/users is documented."
      formatting_intact:
        type: boolean
        description: "True if existing formatting is preserved."
fallbacks:
  max_iterations: 3 # Maximum retries if validation fails
  on_fail: revert # If max_iterations reached, revert all changes.
telemetry:
  tags:

    - documentation

    - api-update
  metrics:

    - success_rate

    - iteration_count

At first glance, this is more verbose than a one-line instruction. But this verbosity is its strength. It makes the task deterministic, auditable, and recoverable.

Mission Objects are stateful artifacts (Executable PR + state of record)

A Mission Object is not only an input to a run. It is the state of record for the unit of work: the thing you can point at in review, rerun tomorrow, and audit later.

In practice, treat the Mission Object as a persistent artifact that the runner updates (or writes a companion record for) as it moves through the loop:

state:
  status: planned # planned | running | completed | failed | reverted
  started_at: null
  completed_at: null
  attempt_count: 0
  last_run_id: null

The Mission stays human-readable, but it stops being prose. It becomes an executable pull request: goal + scope + budgets + gates, plus outcome and a pointer to evidence in the Ledger (diffs, validator output, traces).

The Meta-Patterns Applied to Missions

Mission Objects are where the Meta-Patterns become concrete.

1) Don’t Chat, Compile (Mission Objects are build inputs)

Chat is fine for ideation. For repeatable work, turn intent into an artifact you can version and rerun.

The point is provenance: you can rerun the same Mission Object tomorrow and debug the process, not just the outcome.

2) Physics is Law (the Mission must be gateable)

A Mission Object is only useful if the system can reject it deterministically. That means the Mission must define:

If the gate fails, the correct result is FAIL and a revert.

3) Recursion (Missions are system currency)

Once Missions exist as artifacts, maintenance loops can emit them too.

Now “maintenance” is a pipeline, not a calendar reminder.

Judge Output as Structured Error Objects (Signal-Rich Failure)

Once an AI has attempted a task defined by a Mission Object, how do we evaluate its output? A simple “yes” or “no” isn’t enough. We need signal-rich failure that tells us why something went wrong, enabling intelligent iteration or informed human intervention. This is where the quality_gate field in our Mission Object comes into play.

A quality_gate isn’t just a pass/fail check; it’s a mechanism for producing structured feedback. This feedback should be machine-readable, ideally conforming to a predefined schema.

Consider our README.md update. The quality_gate references validator_script and expected_output_schema. After the AI proposes changes to README.md, our system executes validator_script. This script doesn’t just return an exit code; it produces a JSON object detailing the validation results.

Here’s an example of what that validator script might output if it fails:

{
  "validation_status": "FAILED",
  "reason": "Missing required content or incorrect format.",
  "details": [
    {
      "check": "Must contain '/v2/users'",
      "status": "PASSED"
    },
    {
      "check": "Must contain 'pagination'",
      "status": "PASSED"
    },
    {
      "check": "Regex match for exact line '/v2/users': Supports pagination...",
      "status": "FAILED",
      "actual": "- `/v2/users`: Manages user resources.",
      "expected_pattern": "- `/v2/users`: Supports pagination and user resource management."
    }
  ],
  "recommendations": [
    "Ensure the description for `/v2/users` explicitly mentions 'pagination'."
  ]
}

That JSON output is not just a report. It is the next iteration’s input.

When you feed the failing checks back into the next Refine step (Feedback Injection), the model can fix one constraint instead of re-interpreting the whole mission.

If you revert after a failure, keep the candidate diff and the validator output in a quarantine directory (Salvage Protocol). You want the system safe by default, and you want humans to recover useful fragments without re-running the model.

A concrete Salvage Protocol example: loot the useful fragment

Imagine attempt 2 produced a good helper, but the overall patch failed a validator (wrong edit region, wrong formatting, missing required string). The runner should revert the working tree, but keep the evidence:

.sdac/workflow-quarantine/
  update-readme-api-endpoint-v1/
    attempt-2.diff
    attempt-2.findings.json

On the next attempt, you can either:

Salvage is not “keep broken code.” It’s “keep the best parts of a failed attempt with provenance.”

This structured error object is critical. Instead of a vague “validation failed,” we get:

This is the “deterministic context” that tames stochastic generation. When the AI fails, we get precise signals on how to guide its next attempt, rather than just knowing it failed.

The Adaptation State Machine (ASM)

Receiving structured feedback is powerful, but what do we do with it? This brings us to the Adaptation State Machine: the allowed moves after the Judge step. The fallbacks section in a Mission Object sets boundaries for these moves.

In a minimal implementation, the core moves are:

stateDiagram-v2
  [*] --> Write
  Write --> Judge
  Judge --> Commit: PASS
  Judge --> Iterate: FAIL (signal-rich)
  Judge --> UpdateEnvironment: FAIL (missing constraints/tools)
  Judge --> Revert: FAIL (unsafe or thrashing)
  Iterate --> Write
  UpdateEnvironment --> Write
  Commit --> [*]
  Revert --> [*]
  1. Iterate: try again with tighter constraints and the exact failing evidence. This is only worth doing when the Judge output is signal-rich (file, line, failing check, expected vs actual).

  2. Update Environment: change the deterministic context or tools, not the output. Add a missing schema to the slice. Add a validator. Add a fixture. If the system is failing because it lacks the contract, no amount of mutation fixes it.

  3. Revert: abort and leave the Terrain unchanged. This is a successful outcome when the loop is thrashing or the blast radius is too high.

If the Judge returns PASS, you can commit or merge. If it returns FAIL, the ASM decides whether it’s worth spending more compute or whether you should stop and ask for a better Mission Object.

Templates by Change Type

Not every engineering task is unique. Many fall into predictable patterns: synchronizing documentation, evolving schemas, or performing bounded refactors. Attempting to craft a unique Mission Object for each instance of these common tasks is inefficient and error-prone. This is where Mission Object Templates come in.

Templates pre-package the common goal, scope, constraints, quality_gate, and fallbacks for specific categories of changes. They allow engineers to invoke complex, AI-driven workflows with minimal input, inheriting a robust set of guardrails and validation logic.

Here are examples of how templates can simplify common SDaC workflows:

Driver Pattern (decouple intent from mechanism)

Mission Objects and templates should express what to do, not which tool to use. “Run tests” is intent; pytest vs vitest vs mvn test is mechanism.

One simple pattern is to name the intent and let a driver map it to the repository substrate:

effects:
  - action: run_tests
    target: default

Your driver selects the command based on the repo (language, package manager, existing CI), ideally using Context Graph identity for the target node. That keeps Mission Objects portable across stacks and keeps tool details out of intent.

Driver resolution algorithm (identity → command, fail fast)

Keep driver resolution deterministic and explicit:

  1. Read node identity from the Context Graph (language + build system).
  2. Look up a driver in a registry (built-in defaults plus repo overrides).
  3. If multiple drivers match, apply precedence (most specific match wins).
  4. If no driver matches, fail fast with an actionable error (“no test driver for rust+cargo”).

The key is that the system chooses the mechanism from deterministic identity. The model never guesses which toolchain to run.

Worked polyglot example: pytest vs. jest

Assume a monorepo with two packages:

The Mission Object expresses intent only:

effects:
  - action: run_tests
    target: services/api
  - action: run_tests
    target: services/web

The driver uses deterministic identity (computed from pyproject.toml / package.json) to choose the mechanism:

drivers:
  run_tests:
    - match: {language: python}
      cmd: ["pytest", "-q"]
    - match: {language: javascript}
      cmd: ["npm", "test"] # runs jest via the package's scripts

If services/web has no package.json, the driver fails fast. If services/api has no test runner configured, the driver fails fast. The model never invents toolchains; the substrate tells you what exists.

Documentation Synchronization

Schema Evolution

Bounded Refactor

By using templates, teams can establish robust, pre-vetted patterns for AI-driven changes. This promotes consistency, reduces the risk of novel instruction-injection failures, and allows engineers to focus on higher-level problems, trusting that the underlying SDaC system will execute routine tasks safely and reliably within well-defined boundaries.

Actionable: What you can do this week

  1. Define a Simple Mission Object: Pick a straightforward, low-stakes task in your codebase (e.g., update a comment, add a line to a specific section of a non-critical file). Write a basic YAML file for this task, including goal, scope (targeting a single file), and simple constraints (e.g., must_contain_strings).

  2. Mock an AI Executor: Write a small script (Python, Bash, Node) that reads your Mission Object YAML. For now, have it simply print the goal and scope. Then, manually make the requested change in the target file.

  3. Implement a Basic Quality Gate: Add a quality_gate to your Mission Object. Create a separate script (e.g., validate_task.sh) that takes the modified file as input and performs one or two simple checks (e.g., grep for a specific string, check file size). Have this script output a simple JSON object indicating status (“PASSED” or “FAILED”) and a reason.

  4. Wire up a “Judge”: Modify your executor script from step 2 to call your quality_gate script. Based on the JSON output, print “Mission PASSED” or “Mission FAILED” along with the reason. You now have the core components of an auditable SDaC loop.