Docs / Guides
contract surface · 70-control-plane.md · 71-console.md · guide · card
Operator surfaces
The problem the surfaces solve
An operator configures what runs and asks questions of the results. Two failures
matter most: a configuration edit that races another operator’s edit or arms a broken
schedule, and a model answer that drifts from the stored rows. The control
plane makes edits versioned and validated, and the console makes every answer rest on
rows read under the operator’s own credential.
The hosted console has two pages, Admin for configuration and Query for questions.
Each page and its API routes check a verified identity assertion and that page’s grant
(wrong-pagesurface.open-console.wrong-page A Query or Admin page or API request without a verified Access assertion or Cognito session granting that page raises ConsolePageForbidden and dispatches nothing. ).
Flowchart diagram
Flowchart, top to bottom. 11 nodes, 16 edges.
operator → Query policy [query session]; → Admin policy [admin session]
identity gate: Query policy → Query page [Query grant]
identity gate: Admin policy → Admin page [Admin grant]
operator console: Query page → turn loop [question]
operator console: Admin page → control document [edit, apply]; → workflow canvas [workflow request]
operator console: turn loop → store engine [admitted read tools]; → Query page [prose, widget, sources]
operator console: workflow canvas → Admin page [workflow view]
control document → reconciler [apply, reconcile]; → workflow canvas [pipeline versions]
control plane: reconciler → armed set [arm due work]
control plane: armed set → store engine [fire, dispatch]
store engine → turn loop [governed rows]; → workflow canvas [run outcomes]
identity gate
Query policy
Admin policy
operator console
question
workflow request
prose, widget, sources
workflow view
Query page
Admin page
turn loop
workflow canvas
control plane
arm due work
reconciler
armed set
query session
admin session
Query grant
Admin grant
edit, apply
apply, reconcile
pipeline versions
fire, dispatch
admitted read tools
governed rows
run outcomes
operator
control document
store engine
From document to running job
The operator edits one configuration document. A credential value typed into a field
fails, because the document holds references and the backend holds material
(secret-in-documentsurface.edit.secret-in-document A credential value typed into a configuration field raises SecretMaterialInDocument; the document holds references and the backend holds material. ).
The example adds a nightly fold job for the table record_sync_records. Validation
checks that the target names a produced table (target-unboundsurface.fire.target-unbound A fold target naming no produced table, or a build target naming no declared [[model]], raises JobTargetUnbound at validation. ), and a
document failing validation claims no version (validationsurface.apply.validation A document failing engine validation raises ApplyValidationRefused and claims no version. ).
A second operator applied a minute earlier, so this apply’s compare-and-swap loses. The
surface reloads the winning version and reapplies the pending edit onto it
(version-racesurface.apply.version-race An apply whose compare-and-swap loses raises ManifestVersionConflict, reloads the winning version and reapplies its pending edits onto it, overwriting no applied version. ). Admin edits and applies also need the server’s admin
capability (admin-grantsurface.open-console.admin-grant An Admin edit or apply without a server-held admin capability raises ConsoleAdminGrantMissing and reaches no control call. ).
The reconciler polls the applied document’s pointer and diffs the schedule set
(poll-cadencesurface.reconcile.poll-cadence poll takes a schedule string, and a [control] block declaring none polls every 30 s. ). If a poll fails, the armed set keeps running
unchanged (fail-staticsurface.reconcile.fail-static An unreadable pointer, an unparseable snapshot or a control plane answering 5xx raises ControlSnapshotUnreadable, logs a diagnostic, and leaves the armed set in place running. ).
An unreadable schedule string holds back its own entry alone, and every other entry arms
(unreadable-schedulesurface.arm.unreadable-schedule A schedule string the grammar cannot read, including L, W, ?, # and @ macros, raises ScheduleUnreadable for that entry alone, naming the diagnostic; every other entry of the document arms. ). A job kind outside the closed union fails
validation (job-kind-unknownsurface.fire.job-kind-unknown A block naming a kind outside the union, an argument vector or a host command raises JobKindUnknown at validation. ).
At the job’s instant, dispatch starts one instance in a bounded pool. A worker running
the fold goes silent, and its work moves to another worker under the next attempt
number (heartbeat-lapsesurface.dispatch.heartbeat-lapse A heartbeat lapse past 60 s reschedules that worker's in-flight work onto another matching worker under the next attempt number. ). The silent worker’s late callback carries
the old attempt and changes nothing (callback-rejectedsurface.dispatch.callback-rejected A heartbeat or callback whose signature fails, whose attempt is not the step's current attempt, whose timestamp falls outside callback-skew, or whose step has closed, raises DispatchCallbackRejected and changes no step. ).
A store-driven item whose body names a delegate runs on it: code deployed outside the binary and
reached over one signed HTTP request (bindingsurface.delegate.binding [control.delegates.<name>] binds a delegate by url, a mode of call or submit, and under call a timeout; a store-driven body = { delegate = "<name>" } naming no bound delegate raises JobDelegateUnbound at arm or fire. ). Its answer is a
closed outcome: rows, a deferral, or a transient or permanent failure
(outcomesurface.delegate.outcome An answer is a Answer; ok lands as completion, defer parks, and transient and permanent follow retry-budget. ). A fire refuses a delegate bound in submit mode and names
call, the mode it dispatches (submit-unfilledsurface.delegate.submit-unfilled A store-driven fire whose delegate binds mode = "submit" raises DelegateSubmitUnfilled, naming call as the mode that dispatches, before any execution opens, so no item receives a receipt. ).
A data plane serving from a region its policy omits refuses to start
(region-mismatchsurface.reside.region-mismatch A configured resource resolving to a region the policy omits raises EnforceRegionMismatch at startup, and the runtime serves nothing. ). Served routes create, pause
and delete a tenant’s source connection through versioned claims (createsurface.connection.create A served connection create binds an admitted tenant, principal, source selection, secret name and pipeline id in one versioned control claim under version-race. ).
Admin projects pipelines, jobs and models as one node and edge graph, with each latest
run outcome (workflow-canvassurface.visualize.workflow-canvas Admin projects the store-published pipelines, jobs, models, publications and external steps as one node–edge graph under dataflow-edges, with each pipeline's schedule, steps and latest run outcome. ). Run history lists each recorded run
newest first (run-historysurface.visualize.run-history Admin lists recorded runs newest first, each with its pipeline, table, status, rows and duration, filtered by pipeline, through the server-held control capability. ), and the lineage view draws only what the
operator’s own credential reads (lineage-viewsurface.visualize.lineage-view Admin draws call under the operator's reader credential and shows withheld as a count. ).
A deployment can publish those reads on a Data page outside the gate, read-only (public-datasurface.open-console.public-data A deployment setting CONTEXTFUL_CONSOLE_PUBLIC_DATA to 1 serves Data: GET reads of Admin's workflows, runs, lineage, trace and search, to an anonymous operator, and no record, answer-model or edit route. The setting is off by default. ).
One question in Query
The operator opens Query on the field-notes store. Its registry entry carries no
credential name of its own (authored-namesurface.register-store.authored-name An entry carrying its own credential or binding name raises StoreNameAuthored. ). The server mints
a per-reader credential; a refused mint falls back to a shared credential or surfaces
the refusal (mint-refusedsurface.ground.mint-refused A refused mint raises ConsoleTokenExchangeRefused. A store carrying a shared credential falls back to it; one carrying none surfaces the refusal to the reader. ).
Before any question, Query offers chips built from the tables the reader may describe
(discovered-chipssurface.browse.discovered-chips Query builds pre-question chips from described tables admitted to the reader's zone and humanizes their table labels. ). The operator asks which records arrived this
week. The vantage, the session’s time basis, parses as a calendar day
(unparseablesurface.set-vantage.unparseable A vantage that is neither a calendar day nor an instant raises ConsoleVantageUnparseable, answered 400. ).
A turn plans over admitted read tools, replans once if the first round leaves the
question open, and synthesizes from the governed rows (turn-flowsurface.plan-turn.turn-flow A Query turn plans over admitted read tools, replans once after an unanswered first round, and synthesizes from the resulting governed rows. ).
The server chooses every tool, so a call naming an unadmitted tool dispatches nothing
(unadmitted-toolsurface.ground.unadmitted-tool A call naming a tool the turn's packs do not admit raises ConsoleToolNotAdmitted and dispatches nothing. ).
Prose exists only over a tool result (ungrounded-answersurface.ground.ungrounded-answer A path composing answer prose in a turn holding no tool result raises ConsoleUngroundedAnswer. ). The
server redacts each streamed chunk before Query shows it (stream-redactionsurface.speak.stream-redaction The server redacts each streamed model chunk before Query emits prose to the operator. ).
The rows carry a date column and a measure over distinct days, so component choice draws
a line chart (component-choicesurface.render.component-choice One row with one measure draws a metric; a date column with a measure over 3 rows on distinct days draws a line; anything else draws a table offering a bar view. ). A view arriving from the client
fails, because the server builds every view (client-authored-viewsurface.render.client-authored-view A view specification arriving from a client, or composed by the model, raises ConsoleViewNotServerBuilt. ).
After the answer, a second pass distils the exchange into learnings for later turns
(distillationsurface.learn.distillation After the answer streams, a second pass distils the exchange into at most 3 entries shaped {subject, key, learning}, zero included. ). A greeting at session start appears only when a new row matches
an earlier conclusion within budget (absence-is-earnedsurface.brief.absence-is-earned The card needs a session with no turns, at present time, a live conclusion, an arrived row matching an interest, and a derivation inside its budget. An error or timeout raises ConsoleBriefUnavailable, and no card renders. ).
Posting an answer to a team channel under a service identity, on a schedule, fails
(askerless-audiencesurface.publish-answer.askerless-audience A scheduled job posting to an audience under a service identity raises VisibilityAskerlessAudience, naming the destination; a scheduled post's corpus is narrowed in the reviewed manifest to what the destination reaches. ).
The client library
The client library reaches the engine through four deployment shapes. Its process
transport needs a verified capability token before any protocol framing
(stdio-credentialsurface.package.stdio-credential The process transport requires a verified capability token; --owner selects its signed owner claim. An absent token raises StdioCredentialMissing before protocol framing and never resolves to owner context. ).