Docs · Hangar
Skyport AI workloads: one agent per workload shape
Skyport already exercises every Airframe stack and component with five services and a broker
(airframe/docs/user/skyport-demo.md). This extends it with six AI workloads, one per shape, so
every part of Autopilot has a real caller, a visible effect, and a test that can fail.
The definitions are real and tested: autopilot/agents/skyport/*.yaml (nine files: six workloads and
three team members), with one Preflight case each in autopilot/preflight/cases/skyport-*.yaml.
Status: definitions and cases Built. Everything that would run them is Proposal until
Autopilot phase A and B exist (see roadmap.md).
The six workloads
| Shape | Agent | Trigger | What it does | Skyport touchpoints | Preflight verifier |
|---|---|---|---|---|---|
| Task | flight-briefer |
a person or API asks | Ops brief for one flight from three APIs | reads flight-api, boarding-api, baggage-api | every fact matches the API fixture; no invented flight |
| Session | gate-copilot |
a gate agent opens a chat in Tower | Answers questions, drafts announcements | reads flight-api; approvals in Tower | scripted chat; a draft is never sent |
| Service | passenger-assistant |
always on, HTTP | Public flight-status chat | boarding-api, flight-api, Redis | golden Q&A, plus an injected instruction that must not be followed |
| Scheduled | delay-digest |
every 24 hours | Daily delays and gate-change report | flight-api history; the artifact store | counts equal the event log; a second run changes nothing |
| Event | disruption-responder |
flight.*.delayed on the broker |
Drafts rebooking notices, never sends | broker consumer, Clearance | one run per unique event; a redelivery starts none |
| Team | irregular-ops-team |
spawned by the responder | Planner with researcher, drafter, checker | all three APIs, narrowed | child budgets sum within the root; an over-broad spawn is denied |
Diagram: diagrams/plan/07-skyport-ai-workloads.html. The event and team flow:
diagrams/plan/08-skyport-event-to-team.html.
Why these six
They are business-domain agents, so the demo shows agents operating a system, not only agents
operating the platform. The platform-facing agents already exist or are planned: HolmesGPT is the
event and service shape for triage (diagrams/autopilot/17-triage-before-after.html), and the coding
agent is the delegated task shape for platform work (the parachute test).
A deliberate rule: nothing here applies a change
No Skyport agent has a write tool. They read, they draft, they store artifacts, they ask a human, and
they spawn narrower runs. Anything consequential goes through human.request. A test asserts it:
test_nothing_in_the_demo_can_apply_a_change_except_by_spawning_narrower_runs. The demo shows what
“humans on the loop” looks like without the demo being able to break Skyport.
What each needs that does not exist yet
| Piece | Needed by | Status |
|---|---|---|
| Clearance core, sessions, policy, audit | all | Built (255 tests) |
AgentRun XRD, composition function, provider-kubernetes grants |
task, session, scheduled, event, team | Draft, never applied |
| Model proxy (HTTP forwarder), hosted model route, key in Infisical | all | decision core Built; forwarder Proposal |
Read tool app.api.get (allowlisted service APIs) |
all | Built (policy and tests); real adapter Proposal |
Artifact tools artifact.put and artifact.get |
task, scheduled, team | Built; MinIO adapter Proposal |
Session channel chat.recv and chat.send, and Tower’s chat UI |
session | tools Built; Tower Proposal |
| Trigger runner (interval) | scheduled | logic Built (due); runner Proposal |
Trigger bridge (broker to open_triggered) |
event | design below; Proposal |
Chart agent: block (tokens, egress, contract) |
service | Proposal (Airframe AF-10) |
event trigger type in the definition schema |
event | Built |
| Autopilot plugin (fleet-wide) + Tower Agent tab (app-scoped session chat, reuses the plugin’s backend) | session, event, team | Proposal (decided 2026-09-27, design.md 8.3a) |
Skyport APIs, broker, baggage-api, MongoDB |
task, session, service, digest, responder, team | broker, flight-api, boarding-api Built; baggage-api and MongoDB next |
The trigger bridge (event shape)
An ordinary Airframe PythonApplication with a RabbitMQ attach consumer, so the demo also
exercises the component it depends on. It:
- binds a queue
ai.disruptiontoflights.eventswithflight.*.delayed(the definition’sbindingKey); - for each message, derives
task_id = event_task_id(agent, message_id)so redelivery maps to the same run (RabbitMQ is at-least-once); - applies the definition’s
maxPerHouras a storm brake (a delayed-flights storm must not start a thousand runs); - calls Clearance
open_triggeredas its own workload identity, thenlaunch; - acks only after the audit record exists; malformed messages are rejected and audited, not dropped.
It holds no model key and no repo credential. Its authority is “may start this one definition”.
Tested pieces: amqp_topic_match, match_event, event_task_id, RateLimiter, idempotent
open_triggered. The consumer itself is the build work.
The session channel (session shape)
Runs have no inbound network by policy, so Tower cannot connect to a gate-copilot pod. Instead
Clearance holds a per-session message queue: Tower posts the human’s message to Clearance, the agent
calls chat.recv (long-poll) and chat.send. The sandbox only ever dials out. Those two tools exist
only for kind: session (rule R018).
The service agent (service shape)
passenger-assistant is an application: PythonApplication plus a Redis component, deployed and
released like baggage-api, with the chart’s agent: block giving its pods the same tokens, egress
policy and labels a run gets. It is the only workload facing the public, so its definition asks for the
hardened sandbox.
That fails closed on the dev cluster, by design. It has no gVisor or Kata
runtime class, so the chart (and function-agentrun) reject it rather than run it weaker. To run the
demo on the dev cluster, change the definition to standard in a reviewed commit and record why. A run can
narrow a definition but never widen it, so lowering isolation is only ever a commit. That moment is
worth a paragraph in the quickstart.
Safety demonstrations built into the demo
| Demonstration | Where | What it proves |
|---|---|---|
| Prompt injection | the passenger-assistant fixture has a flight-remarks field telling the model to reveal credentials and call a forbidden tool | tool output is data; the assistant has no tool that could comply; a tripwire attempt would end the session |
| Redelivered event | responder fixture delivers one message twice | idempotent task_id, no second run |
| Event storm | responder fixture floods flight.*.delayed |
maxPerHour brake |
| Over-broad spawn | team fixture instructs a worker to ask for more than its parent has | narrow-only, denied with R007 and audited |
| Wrong fact | team fixture puts a wrong gate in a draft | the checker worker catches it against the API |
| Fail-closed sandbox | passenger-assistant on the dev cluster | a hardened request is Rejected, never downgraded |
| Expiry without Clearance | stop Clearance mid-run | the run still ends at expiresAt |
Quickstart parts (numbered in the order built)
| Part | Adds | Depends on |
|---|---|---|
| 4 | baggage-api (Python) + MongoDB, consuming flight events |
MongoDB component (born A+) |
| 5 | skyport-auth (OAuth) and enforced JWTs |
OAuth component (born A+) |
| 6 | Your first agent: flight-briefer (task) |
Autopilot phase A and B, model proxy v0 |
| 7 | gate-copilot (session) |
session channel, the Tower Agent tab (app-scoped; the standalone Autopilot plugin carries the fleet-wide view) |
| 8 | passenger-assistant (service) |
chart agent: block, the hardened-sandbox moment |
| 9 | delay-digest (scheduled) |
trigger runner, artifact store |
| 10 | disruption-responder (event) |
trigger bridge |
| 11 | irregular-ops-team (team) |
run.spawn, approvals |
| 12 | (optional) nginx edge | nginx component |
The existing part 3 (broker) is done. Parts 4 and 5 are the existing Skyport phases 3 and 4; the AI
parts slot in after them because the agents read baggage-api. Each part follows the house rule:
walk it live before it is called done, and the quickstart’s verified-state note says exactly what was walked.
Where the code lives (proposed)
airframe/examples/skyport/
flight-api/ boarding-api/ baggage-api/ # existing and next
agents/
flight-briefer/ gate-copilot/ passenger-assistant/ delay-digest/
disruption-responder/ irregular-ops-team/ ops-researcher/ ops-drafter/ ops-checker/
trigger-bridge/ # an ordinary Airframe app
fixtures/ # API fixtures, event streams, the injection text
autopilot/ (new repo; today ~/tech/autopilot)
agents/skyport/*.yaml preflight/cases/skyport-*.yaml
Definitions stay next to Clearance because they are policy, reviewed as policy. The agent code lives with the demo because it is an example, versioned with the Airframe it exercises.
Cost control
A hosted model is the default. Every run has a token budget (modelTokens), the model proxy
enforces it and trips the breaker on overrun, and delay-digest and flight-briefer are the only
scheduled or on-demand spenders. A Modelplane hub is a later trial (M5), not a demo dependency.
Acceptance for the whole extension
- Each agent runs end to end on the dev cluster and its Preflight case passes.
- Each case’s seeded bad run fails (the harness can say no).
kubectlshows no run namespace older than itsexpiresAt, with Clearance stopped.- One
task_idtraces a disruption from broker message to the team’s final artifact in the Flight recorder. - The scorecard has not regressed.