Handles and execution¶
For work that outlives the call that started it. A handle is a value you can store and come back to from another process — polling it, or cancelling it.
gantry.ExecutionHandle
dataclass
¶
A durable reference to one engine job, safe to store and return to.
gantry_id identifies the run to Gantry and native_id identifies it to
the engine; keeping both is what makes reconnection possible after the
submitting process is gone. All four identifying fields must be non-empty
and submitted_at must be timezone-aware, enforced at construction.
Source code in gantry/handle.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
gantry.Execution
dataclass
¶
Gantry's normalized live view of an engine job.
Source code in gantry/execution.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | |
gantry.ExecutionState
¶
Bases: StrEnum
Gantry's normalized job states, mapped from each engine's own.
UNKNOWN is a real state, not an error case: it means Gantry could not
establish what happened, which a caller must treat differently from a known
failure because the work may still be running.
Source code in gantry/execution.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | |
gantry.ValidationResult
dataclass
¶
What the engine's own planner said about a statement before running it.
This is native validation — EXPLAIN or the engine's validate endpoint —
not Gantry's policy check. errors makes a statement inadmissible;
warnings do not. Build one with accepted() or rejected().
Source code in gantry/execution.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |
Running work¶
The module-level functions act on a process-default control plane. run
submits and waits; the others exist for work that outlives the call.
gantry.submit
async
¶
submit(
artifact: Artifact,
*,
target: ExecutionTarget,
context: Context,
policy: PolicyRequirements,
) -> ExecutionHandle
Validate, admit and start an artifact, returning a durable handle.
The handle outlives this call, so work can be polled or cancelled from another process. Admission happens here: a policy the adapter cannot enforce is refused before anything runs.
Source code in gantry/runtime.py
461 462 463 464 465 466 467 468 469 470 471 472 473 474 | |
gantry.wait
async
¶
wait(
handle: ExecutionHandle,
*,
verify: Sequence[Verifier] = (),
poll_interval_seconds: float = 1.0,
) -> Result
Block until an execution finishes, then verify it.
A terminal engine state is not the answer on its own — the verifiers decide
whether the result may be believed, and their outcome is part of the
Result.
Source code in gantry/runtime.py
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | |
gantry.run
async
¶
run(
artifact: Artifact,
*,
target: ExecutionTarget,
context: Context,
policy: PolicyRequirements,
verify: Sequence[Verifier] = (),
poll_interval_seconds: float = 1.0,
) -> Result
Submit an artifact and wait for it, returning the verified result.
The common case. Use submit and wait separately when the work outlives
the request that started it.
Source code in gantry/runtime.py
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | |
gantry.get
async
¶
get(handle: ExecutionHandle) -> Execution
Return the current Execution for handle without waiting.
Refreshes state from the adapter registered for the handle's target. Never
raises: a missing adapter, an adapter that fails, or a reply for a
different handle all come back as an Execution in state UNKNOWN
carrying the reason as its failure.
Source code in gantry/runtime.py
477 478 479 480 481 482 483 484 485 | |
gantry.cancel
async
¶
cancel(
handle: ExecutionHandle, *, mode: str = "default"
) -> Execution
Ask the adapter owning handle to stop that execution.
mode is passed through to the adapter; engines that distinguish a
graceful stop from a hard kill interpret it. Returns the Execution the
adapter reports after the request, which may still be running if the
engine stops asynchronously. Like get, a missing or failing adapter
yields an Execution in state UNKNOWN rather than an exception.
Source code in gantry/runtime.py
507 508 509 510 511 512 513 514 515 516 | |
gantry.configure
¶
configure(
*,
adapters: Mapping[str, ExecutionAdapter],
store: ExecutionStore | None = None,
) -> None
Replace the default control plane's adapters and execution store.
Source code in gantry/runtime.py
453 454 455 456 457 458 | |
gantry.register_adapter
¶
register_adapter(
target_kind: str, adapter: ExecutionAdapter
) -> None
Register adapter on the process-default control plane for target_kind.
Raises ValueError if the target kind is empty. A later registration for
the same kind replaces the earlier one.
Source code in gantry/runtime.py
444 445 446 447 448 449 450 | |
The control plane¶
Construct one directly to keep adapters and execution history isolated from the process default — two control planes do not see each other's runs.
gantry.ControlPlane
¶
Routes executions to adapters by target kind and records their state.
Holds the adapter registry and the execution store behind the
module-level submit/get/wait/cancel/run helpers. Construct one
directly to keep adapters and history isolated from the process default.
Source code in gantry/runtime.py
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | |
gantry.ExecutionStore
¶
Bases: Protocol
Persistence for RunRecords, keyed by gantry_id.
Implement this to let handles outlive the process that created them. The
default is MemoryExecutionStore, which does not.
Source code in gantry/store.py
35 36 37 38 39 40 41 42 43 44 | |
gantry.RunRecord
dataclass
¶
Everything needed to resume governing a run in another process.
Persisted at submission. It keeps the artifact, policy and admission decision beside the handle, because reconnecting to a job is not enough: deciding whether to accept its result requires knowing what was promised when it was admitted.
Source code in gantry/store.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
gantry.MemoryExecutionStore
¶
Process-local store for development and tests.
Source code in gantry/store.py
47 48 49 50 51 52 53 54 55 56 57 | |
Adapters and targets¶
gantry.ExecutionAdapter
¶
Bases: Protocol
The contract an engine backend implements to be governed by Gantry.
Six methods: declare what you can enforce (capabilities), ask the engine
to check a statement (validate), start it (submit), report on it
(status), collect it (result), and stop it (cancel). Gantry stays out
of the data path — result returns references to where the engine wrote,
not the rows.
Source code in gantry/adapter.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
gantry.ExecutionTarget
dataclass
¶
The engine-specific environment in which an artifact should run.
Source code in gantry/target.py
10 11 12 13 14 15 16 17 18 19 | |
gantry.AdapterCapabilities
dataclass
¶
What an adapter can actually enforce, declared rather than assumed.
Admission compares this against PolicyRequirements: a requirement with no
matching capability is refused, because a bound the engine never applies is
worse than no bound. Every field defaults to false, so a new adapter is
trusted with nothing until it says otherwise.
Source code in gantry/capabilities.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
gantry.Artifact
dataclass
¶
An opaque payload produced by an agent or another upstream system.
Source code in gantry/artifact.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
gantry.Context
dataclass
¶
Resources and metadata made available for one run.
Source code in gantry/context.py
10 11 12 13 14 15 | |
gantry.Tool
dataclass
¶
A named JSON-schema operation that an agent framework can invoke.
Source code in gantry/tool.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
invoke
async
¶
invoke(
arguments: Mapping[str, object] | None = None,
/,
**keyword_arguments: object,
) -> T
Invoke the trusted handler with model-supplied arguments.
Source code in gantry/tool.py
19 20 21 22 23 24 25 26 27 28 29 30 | |
Admission¶
The gate between proposing and executing: a policy the adapter cannot enforce is refused rather than warned about.
gantry.PolicyRequirements
dataclass
¶
What the application demands of an execution, independent of engine.
Written by application code, never by the agent. The require_* fields
name a capability the adapter must have for the work to be admitted at all.
Construction rejects a self-contradictory policy (read_only with
allow_writes) and non-positive limits, so an impossible policy fails where
it is written rather than at admission.
Source code in gantry/policy/requirements.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
gantry.admit
¶
admit(
validation: ValidationResult,
capabilities: AdapterCapabilities,
policy: PolicyRequirements,
) -> AdmissionDecision
Decide whether an artifact may run, given what the adapter can enforce.
The decision is the gate between proposing and executing. A policy the adapter cannot enforce is a refusal rather than a warning: a bound nobody applies is worse than no bound, because callers act as though it held.
Source code in gantry/admission.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
gantry.AdmissionDecision
dataclass
¶
The outcome of admit: whether this artifact may be submitted.
reasons carries every refusal, not just the first, so a caller sees all
of what would have to change. capabilities is kept because later stages
need to know what was promised — wait uses it to decide whether Gantry
must enforce a runtime limit the engine cannot.
Source code in gantry/admission.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
Engine results¶
What the engine reported, before verification decides whether to accept it.
Most callers read Result instead; this is the adapter-facing envelope.
gantry.ExecutionResult
dataclass
¶
An engine completion envelope; success still requires verification.
Source code in gantry/execution.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |