The engine plans. Nobody else over-plans. 🧭
The obvious way to build a travel assistant is one fixed checklist: flights, hotel, restaurants, activities — every time, even when you only wanted flights. VDX never builds that recipe. It keeps a library of independent capabilities and lets an intent engine compose the smallest valid graph that satisfies your stated outcome.
State an outcome
“Flights Dubai to Paris on November 12. Nothing else.” The “nothing else” is a first-class input — explicit exclusions shape the plan as much as requests.
Composes and validates
Works backwards from what you asked for to the capabilities that can provide it, snaps their typed outputs to inputs, and proves the plan is legal before anything runs.
Touch the real world
Typed plugs behind stable interfaces: a flight API, a hotel API, a browser acting for you. Reads run under permissions; writes stop at an approval gate.
Small bricks, exact labels 🧱
Each capability does one job, declares what it gives and needs in versioned types, and names the adapter that makes it real. Six ship in this reference set — all mock-backed. The full catalog sketches ~40 more across route, flights, event, stay, dining, entry, payment and coordination.
Can this trip even go by air? Resolves origin/destination into a route plus transport feasibility.
Requires origin, destination, departure date. Produces flight.options for ranking.
Orders raw options by constraints first, then soft preferences like cabin class.
Needs a place and dates; a conference venue improves ranking — but is never assumed.
Availability near a point for a party size and time — bookability, not just listings.
Resolves an event into dates and venue with provenance — missing dates force a clarification.
The write-side brick: separately registered, requires a revalidated offer and an approval token — an offer quote is not a ticket.
| Field on every brick | What it tells the engine |
|---|---|
| provides / requires | The typed outputs it hands over and typed inputs it needs — bricks only fit when the types match |
| optional inputs | Preferences that improve results but must never be inferred into existence (cabin class, max price) |
| preconditions & permissions | Eligibility rules and the least-privilege scopes an execution requires |
| side effect | read_only vs write — writes get all the ceremony in Chapter 05 |
| output_ttl_seconds | How long a result stays trustworthy before it must be revalidated |
| execution & adapter | Timeout, bounded-exponential retry, and the allowlisted adapter implementation |
One brick is one small file 📇
A capability's contract is a recipe.yaml manifest plus versioned JSON schemas for its input and output. The YAML is data to review, never code to run — the registry normalizes it to canonical JSON and validates it before any adapter is loaded.
# travel.flight.search @ 1.0.0 id: travel.flight.search version: 1.0.0 kind: atomic provides: [flight.options] requires: [origin, destination, departure_date] optional: [preferences, constraints] dependencies: [travel.route.resolve] side_effects: none permissions: [travel.read] adapter: "mock:travel.flight.search" input_schema: schemas/flight-search.input.schema.json output_schema: schemas/flight-search.output.schema.json timeout_ms: 15000
# what the engine reads out of it Gives: a set of flight options Needs: where from, where to, a departure date Nice to have: cabin, budget, airlines — but the engine must not invent them Wants first: route.resolve ran (don't search for an impossible route) Touches nothing: pure lookup Trusts: only the allowlisted mock adapter — manifests can't smuggle instructions
From a sentence to a legal plan 🧩
Everything left of execution is deterministic. An LLM may propose modules; validation must approve the graph — types, cycles, permissions and side effects — before a single node runs. Selection states matter: a brick being available is never a reason it was selected.
↔ scroll the diagram sideways
goals = extract_goals_and_scope(intent) # outcomes + explicit exclusions facts = resolve_authorized_context(context) # provenance & freshness tracked candidates = registry.find_providers_for(goals.outputs) eligible = policy_and_precondition_filter(candidates, facts) graph = backward_chain_dependencies(eligible, facts) # typed outputs → required inputs graph = remove_unneeded_and_excluded_nodes(graph, goals) # "that's all" cuts here graph = resolve_alternatives_and_conflicts(graph) if graph.missing_critical_inputs: request_minimal_clarification() # ask, never invent validate_types_cycles_permissions_effects(graph) return graph.with_approval_gates_and_provenance()
Artifacts, not prose 🔗
The flight pipeline reads like an assembly line because each stage's output is a versioned artifact the next stage's input schema can check — each carrying provenance, creation time, correlation ID and expiry. And every step has a state the engine can reason about.
Read-only branches with independent inputs run concurrently — hotel search does not wait for flight ranking. Device actions (a browser clicking through a provider) get the same skepticism as APIs: a successful click is evidence, not proof.
Search is free. Buying is not. 🚦
Every read-only brick runs under permission scopes without ceremony. The moment a brick can change the real world — purchase, reserve, cancel, pay — it is a different species: separately registered, explicitly authorized, idempotent, audited, and verified against the provider.
- Explicit approval — no purchase, reservation or cancellation happens without a required ApprovalToken granted for exactly that action.
- Revalidation before commitment — the offer is revalidated at the moment of purchase; a quoted fare that went stale is not a ticket waiting to be claimed.
- Idempotency keys — where providers support them, retries cannot double-charge; where they don't, the outcome is reconciled, not assumed.
- Verified completion — the executor marks success only after independently confirming the ticket or confirmation exists.
Five phrases, five graphs 🧭
The same brick library, composed differently by what you actually said. Notice what is absent in each — absence is the whole point.
Flights only
route.resolve → flight.search → flight.rank → options.present
Not selected: hotels, restaurants, activities, ground transport, booking.Conference + hotel
conference.context → arrival deadline → flight.search
venue → hotel.search near venue → cross-check dates → present
Friend's place
flight branch unchanged · hotel branch never composed
Privacy default: friend's neighborhood only if commute planning needs it; exact address never requested.Dinner only
venue.resolve → restaurants.discover → availability → select → confirm → reserve → verify
Not selected: the entire flight and hotel machinery. Separate intent, separate graph.Mid-plan change
invalidate arrival feasibility + rankings → recompute affected nodes only
Preserved: unrelated confirmed artifacts. Existing tickets are never auto-cancelled.
{
"plan_id": "plan_123",
"intent": "travel.conference_trip",
"scope": ["flights", "hotel"],
"excluded": ["activities", "restaurants"],
"nodes": [
{ "id": "event", "capability": "event.resolve", "depends_on": [] },
{ "id": "route", "capability": "route.resolve", "depends_on": [] },
{ "id": "flights", "capability": "flights.search", "depends_on": ["event", "route"] },
{ "id": "hotels", "capability": "hotels.search", "depends_on": ["venue"] },
{ "id": "review", "capability": "plan.present", "depends_on": ["flights", "hotels"] }
],
"approval_gates": ["before_purchase"],
"status": "planned"
}
In the box — and on the roadmap 📦
An illustrative reference, deliberately mock end-to-end: the contracts and the test list are real, the inventory is not. The repo pairs this page with the full engineering handover and a plain-language explainer.
| Path in the repo | What lives there |
|---|---|
| docs/01-eli5-overview.md | The whole model in plain language — the LEGO framing this page follows |
| docs/02-architecture.md | Engineering handover: composition algorithm, execution states, module map, acceptance tests |
| docs/03-capability-contracts.md | File formats, composition rules, security and submission requirements |
| capabilities/ · schemas/ | The six sample manifests and their typed input/output contracts |
| examples/ | Intents, composed DAG plans, a mock adapter response, a stay-change patch |
| tests/ · SUBMISSION_CHECKLIST.md | What a compliant engine must pass, and what a team submission must include |
Four phases before this touches a real airline
- Contracts — finalize the manifest schema, typed artifacts, selection states, side-effect taxonomy and approval contract; implement five read-only travel capabilities.
- Planner — backward chaining, dependency DAG, scope exclusions, hard/soft constraints, explainable selection; test flight-only vs. conference vs. friend scenarios.
- Runtime — scheduler, adapters, event store, verification, bounded retries, reconciliation, idempotency.
- Extensions — hotels, dining, calendar, device execution, composite templates; measured on task completion, excess invocations, clarification burden, cost and verified success rate.