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:
Scope: Does it only touch that section, or can it add new sections?
Constraints: What format should the description take? Are there keywords it must include?
Quality: How do we know it did a good job? Is it grammatical? Is the new endpoint actually present?
Recovery: What if it messes up the existing formatting? How do we fix it?
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_countAt 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: nullThe 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.
Store the Mission Object as data (
missions/update_readme.mission.yaml) and treat it like source code.Run a deterministic runner that produces a diff plus a Decision Trace (validator output, structured errors, and final
PASS/FAIL).
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:
A bounded scope.
Executable gates (constraints, a
quality_gate, and/or tests).A revert policy when the gate fails.
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.
A Sensor emits a concrete entropy signal (drift, unused config, missing docs).
The loop writes a Mission Object that scopes the repair and defines gates.
The same runner executes it and records evidence in the Ledger.
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:
- include the helper in the deterministic slice (so the model reuses it), or
- copy the helper into the final patch manually, without re-running the model.
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:
A clear
validation_status.A high-level
reason.Granular
detailsfor each check, includingactualvs.expected_patternfor specific failures.Actionable
recommendations.
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 --> [*]
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).
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.
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: defaultYour 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:
- Read node identity from the Context Graph (language + build system).
- Look up a driver in a registry (built-in defaults plus repo overrides).
- If multiple drivers match, apply precedence (most specific match wins).
- 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:
services/apiis Python (tests run viapytest)services/webis JavaScript (tests run viajest)
The Mission Object expresses intent only:
effects:
- action: run_tests
target: services/api
- action: run_tests
target: services/webThe 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 scriptsIf 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
Problem:
README.mdor API documentation often drifts out of sync with the actual code or OpenAPI specification.Template: A
DocSyncMissiontemplate might takesource_file(e.g.,api/openapi.json) andtarget_file(e.g.,docs/api.md) as input. Itsconstraintswould ensure specific sections of the target file reflect the source, and itsquality_gatewould validate format, content, and completeness.# Example template_name: DocSyncMission inputs: source_path: api/openapi.json target_path: README.md section_map: - source_selector: "$.paths['/v2/users']" target_section: "## API Endpoints: /v2/users" # Inherits default constraints, quality_gate for doc sync
Schema Evolution
Problem: Adding a field to a database schema requires corresponding updates in ORM models, API DTOs, and potentially UI forms.
Template: A
SchemaEvolutionMissiontemplate could taketable_nameandnew_field_definitionas input. Itsscopewould be automatically expanded to include relevant files (*.pyfor SQLAlchemy models,*.gofor Go structs,*.tsfor TypeScript interfaces). Itsconstraintsmight enforce type consistency, and itsquality_gatecould run linters or even compile the updated code.# Example template_name: SchemaEvolutionMission inputs: table: users add_field: name: last_login_at type: datetime nullable: true # Inherits default scope, constraints, quality_gate for schema updates
Bounded Refactor
Problem: Applying a consistent rename across a codebase or extracting a small helper function.
Template: A
BoundedRefactorMissionmight takeoriginal_identifierandnew_identifierfor a rename, ortarget_code_blockandnew_function_namefor an extraction. Itsscopewould be tightly limited, and itsquality_gatewould likely involve running unit tests and static analysis.# Example template_name: BoundedRefactorMission inputs: refactor_type: rename_variable original_name: 'user_id_str' new_name: 'user_uuid' files_to_scan: - 'src/auth/**/*.py' # Inherits default constraints (e.g., no functional change, tests pass), quality_gate
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
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 simpleconstraints(e.g.,must_contain_strings).Mock an AI Executor: Write a small script (Python, Bash, Node) that reads your Mission Object YAML. For now, have it simply print the
goalandscope. Then, manually make the requested change in the target file.Implement a Basic Quality Gate: Add a
quality_gateto 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.,grepfor a specific string, check file size). Have this script output a simple JSON object indicatingstatus(“PASSED” or “FAILED”) and areason.Wire up a “Judge”: Modify your executor script from step 2 to call your
quality_gatescript. 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.