{"schemaVersion":"mappls.journey-workshop.v1","slug":"governed-spatial-agent-run","journeySlug":"governed-spatial-agent-run","title":"Build Governed spatial agent run","summary":"An eight-lab, source-bounded workshop for the complete agent run lifecycle: exact commands and events, durable records, replay, concurrency, unknown outcomes, hostile fixtures, a maintained capstone, and production exit evidence.","duration":"2 hr 10 min","level":"Advanced","productSlug":"ai-location","stateModel":"stateful","aggregate":"agent run","actorCount":5,"stateCount":7,"transitionCount":6,"eventCount":6,"sourceGuideSlugs":["mappls-rest-apis","mappls-intouch-rest-apis"],"contractSlugs":["core-location-get-api-places-search-json-autosuggest-api","core-location-get-api-places-nearby-json-nearby-api","core-location-get-rest-key-resources-profile-geopositions-routing-api","intouch-get-devices-gets-the-live-data-of-devices"],"relatedTutorialSlugs":[],"sample":{"slug":"spatial-agent","name":"Spatial Operations Agent","downloadPath":"/downloads/spatial-agent.zip","checksumPath":"/downloads/spatial-agent.zip.sha256","verifiedTestCount":15,"runCommand":"npm test --workspace @mappls-example/spatial-agent"},"labs":[{"slug":"model-lifecycle","title":"Model the lifecycle before the UI","duration":"15 min","objective":"Turn the agent run blueprint into an explicit aggregate boundary owned by the application.","build":["asked: A stable run records the user's objective, scenario, actor, tenant, purpose, and bounded inputs before any provider access.","planned: A schema-valid allow-listed dependency graph, risk class, scopes, argument bounds, and canonical plan hash are available for inspection.","approved: An attributable person approved the exact plan hash, every required scope, purpose, data boundary, policy version, and expiry.","executing: A service-side lease owns execution and calls each Mappls tool only after dependencies and authorization are satisfied.","completed: The grounded answer, structured outputs, exact tool evidence, provenance, citations, cost, and terminal audit event are committed.","rejected: A named approver denied the proposed plan with an attributable reason and no provider calls occurred.","failed: Execution stopped with a typed, secret-safe error and retained evidence for every completed step."],"prove":["Every persisted state exists in the reviewed blueprint.","Terminal states reject ordinary forward commands.","Recovery text is operational guidance, not another hidden state."]},{"slug":"command-event-contract","title":"Implement every command and event pair","duration":"20 min","objective":"Make intent, actor authority, allowed source state, committed state, and emitted fact reviewable together.","build":["ask by Application user: new aggregate → asked; emit agent.question_received.","create_plan by Agent planner: asked → planned; emit agent.plan_created.","approve_plan by Human approver: planned → approved; emit agent.plan_approved.","reject_plan by Human approver: planned → rejected; emit agent.plan_rejected.","start_execution by Execution service: approved → executing; emit agent.execution_started.","complete_execution by Execution service: executing → completed; emit agent.execution_completed."],"prove":["ask resolves to governed-spatial-agent-run-agent-question-received without claiming a provider webhook payload.","create_plan resolves to governed-spatial-agent-run-agent-plan-created without claiming a provider webhook payload.","approve_plan resolves to governed-spatial-agent-run-agent-plan-approved without claiming a provider webhook payload.","reject_plan resolves to governed-spatial-agent-run-agent-plan-rejected without claiming a provider webhook payload.","start_execution resolves to governed-spatial-agent-run-agent-execution-started without claiming a provider webhook payload.","complete_execution resolves to governed-spatial-agent-run-agent-execution-completed without claiming a provider webhook payload."]},{"slug":"durable-records","title":"Persist restart-safe records","duration":"15 min","objective":"Separate business identity, provider evidence, command receipts, immutable facts, audit, and downstream delivery.","build":["Agent run: Durable objective, lifecycle, version, risk, actor, tenant, and purpose boundary. Keys: runId, state, version, tenantId, question, risk.","Plan manifest: Canonical tool graph, dependencies, arguments, requested scopes, and integrity identity. Keys: planVersion, planHash, plannerVersion, steps, requiredScopes, policyVersion.","Approval grant: Independent attributable authority for one exact plan and bounded time window. Keys: planHash, approvedBy, approvedScopes, purpose, approvedAt, expiresAt.","Tool evidence: Resolved arguments, structured response, provider provenance, status, timing, and stable request identity. Keys: toolCallId, stepId, tool, requestId, provenance, completedAt.","Grounded result: Answer and machine-readable outputs with per-step citations and inference labels. Keys: runId, answer, citations, generatedAt, modelVersion."],"prove":["Process restart restores the same aggregate version and command result.","Opaque SDK or native UI objects are not durable records.","Provider evidence and application decisions remain distinguishable."]},{"slug":"concurrency-replay","title":"Make concurrency and replay deterministic","duration":"15 min","objective":"Apply optimistic expected versions and aggregate-scoped idempotency before executing effects.","build":["The model never receives Mappls or customer credentials.","Only schema-valid allow-listed tools and bounded arguments can enter a plan.","Approval names the canonical immutable plan hash and every required scope.","An expired, superseded, partially scoped, or self-approved plan cannot execute.","Every factual provider claim is traceable to retained Mappls provenance.","Tool failures and persisted evidence never expose secrets."],"prove":["An exact replay returns the first result without another event or version.","A reused key with different intent conflicts.","A stale expected version changes no durable truth."]},{"slug":"effects-reconciliation","title":"Control effects and unknown outcomes","duration":"15 min","objective":"Commit outbox intent atomically, execute effects outside the transaction, and reconcile ambiguous results.","build":["Prompt or retrieved content asks for an unapproved tool: detect with The proposed tool, scope, host, or argument is absent from the validated plan policy. Recover with Reject the plan or stop execution and surface the exact policy denial for human review.","Plan changes after approval: detect with Recomputed canonical hash differs from the approved plan hash. Recover with Refuse execution, append a tamper or supersession event, and require a new review.","Provider call succeeds but the worker loses its response: detect with Execution lease expires with an ambiguous step and stable request identity. Recover with Reconcile by provider or application request identity before retrying, especially for side-effecting tools.","Tool output lacks provenance or contains a secret: detect with Response-envelope validation or redaction policy fails. Recover with Quarantine the output, stop the run safely, rotate any exposed secret, and retain only a sanitized incident record."],"prove":["A timeout remains an unknown outcome until identity-based reconciliation completes.","Retries are bounded and preserve the original business and command identities.","Dead-letter or manual review retains the entire attempt history."]},{"slug":"hostile-scenarios","title":"Run all hostile fixture scenarios","duration":"15 min","objective":"Exercise the success path plus replay, concurrency, state, and response-loss failures without an account.","build":["Complete journey: Commit the shortest reviewed success path to the journey-specific operating target.","Idempotent replay: Repeat one command identity and prove that version, event identity, and side effects do not duplicate.","Stale version: Reject a command based on an outdated aggregate version without changing durable truth.","Invalid transition: Reject a known command when the current state does not permit it.","Unknown outcome recovery: Reconcile after a lost response, then replay the original command identity safely."],"prove":["All fixture checks pass for all five scenarios.","Rejected commands emit no event and do not increment version.","The fixture makes zero provider calls and exposes no write tool."]},{"slug":"maintained-capstone","title":"Trace the Spatial Operations Agent capstone","duration":"20 min","objective":"Follow the maintained source through domain rules, adapter seam, repository transaction, HTTP boundary, UI evidence, and restart test.","build":["Run the app's declared test suite (15 tests).","Run fixture mode without a credential.","Inspect audit and outbox evidence after each transition.","Restart the process and continue the same aggregate."],"prove":["The downloadable archive checksum verifies before execution.","The capstone covers the journey target without inventing provider completion.","Browser and HTTP surfaces report the same durable version."]},{"slug":"production-exit","title":"Qualify the real integration boundary","duration":"15 min","objective":"Replace only reviewed adapter seams and collect independent production evidence without weakening application invariants.","build":["Runs and time spent in asked, planned, approved, and executing states","Approval, rejection, expiry, and scope-reduction rate by risk class","Plan hash mismatch, policy denial, and prompt-injection detection","Tool latency, quota, retry, reconciliation, and cost by operation","Provider provenance and citation coverage","Sensitive-location access by tenant, actor, purpose, and retention class","Execution leases, orphaned attempts, outbox backlog, and restart recovery"],"prove":["Exact product entitlement and regional behavior are validated separately.","Provider contract tests cover success, rejection, throttling, timeout, and unknown outcome.","Security, privacy, operations, rollback, and product owners approve exact evidence.","Fixture completion is never presented as provider or production completion."]}],"codeSamples":[{"language":"typescript","label":"TypeScript aggregate boundary","code":"type State = \"asked\" | \"planned\" | \"approved\" | \"executing\" | \"completed\" | \"rejected\" | \"failed\";\ntype CommandName = \"ask\" | \"create_plan\" | \"approve_plan\" | \"reject_plan\" | \"start_execution\" | \"complete_execution\";\n\ntype Command = {\n  name: CommandName;\n  aggregateId: string;\n  expectedVersion: number;\n  idempotencyKey: string;\n};\n\nconst transitions = {\n  \"ask\": { from: [null], to: \"asked\", event: \"agent.question_received\" },\n  \"create_plan\": { from: [\"asked\"], to: \"planned\", event: \"agent.plan_created\" },\n  \"approve_plan\": { from: [\"planned\"], to: \"approved\", event: \"agent.plan_approved\" },\n  \"reject_plan\": { from: [\"planned\"], to: \"rejected\", event: \"agent.plan_rejected\" },\n  \"start_execution\": { from: [\"approved\"], to: \"executing\", event: \"agent.execution_started\" },\n  \"complete_execution\": { from: [\"executing\"], to: \"completed\", event: \"agent.execution_completed\" },\n} as const;\n\nexport function decide(current: { state: State | null; version: number }, command: Command) {\n  const rule = transitions[command.name];\n  if (command.expectedVersion !== current.version) throw new Error(\"version_conflict\");\n  if (!rule.from.includes(current.state as never)) throw new Error(\"invalid_transition\");\n  return {\n    state: rule.to as State,\n    version: current.version + 1,\n    event: rule.event,\n    idempotencyKey: command.idempotencyKey,\n  };\n}\n\n// Persist the result, immutable event, audit row, and outbox intent atomically.\n// Store the first result by idempotencyKey before executing another effect."},{"language":"sql","label":"SQL durability skeleton","code":"CREATE TABLE journey_governed_spatial_agent_run (\n  aggregate_id text PRIMARY KEY,\n  state text NOT NULL,\n  version bigint NOT NULL CHECK (version > 0),\n  updated_at timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE TABLE journey_governed_spatial_agent_run_commands (\n  aggregate_id text NOT NULL REFERENCES journey_governed_spatial_agent_run(aggregate_id),\n  idempotency_key text NOT NULL,\n  request_hash text NOT NULL CHECK (length(request_hash) = 64),\n  committed_version bigint NOT NULL,\n  result_json jsonb NOT NULL,\n  PRIMARY KEY (aggregate_id, idempotency_key)\n);\n\nCREATE TABLE journey_governed_spatial_agent_run_outbox (\n  event_id text PRIMARY KEY,\n  aggregate_id text NOT NULL,\n  aggregate_version bigint NOT NULL,\n  event_type text NOT NULL,\n  payload jsonb NOT NULL,\n  published_at timestamptz\n);\n\n-- In one transaction: lock aggregate, compare version, decide, append audit/event,\n-- insert the outbox row, and remember the exact command result."},{"language":"curl","label":"Complete fixture journey","code":"curl --request POST 'https://developer.mappls.com/api/journey-simulator' \\\n+  --header 'content-type: application/json' \\\n+  --data '{\"journey\":\"governed-spatial-agent-run\",\"scenario\":\"complete-journey\"}'"},{"language":"curl","label":"Unknown-outcome drill","code":"curl --request POST 'https://developer.mappls.com/api/journey-simulator' \\\n+  --header 'content-type: application/json' \\\n+  --data '{\"journey\":\"governed-spatial-agent-run\",\"scenario\":\"unknown-outcome\"}'\n\n# Reconcile agentrun identity and the original idempotency key.\n# Never mint a replacement key merely because the response was lost."},{"language":"json","label":"First command envelope","code":"{\n  \"command\": \"ask\",\n  \"aggregateId\": \"fixture-governed-spatial-agent-run-001\",\n  \"expectedVersion\": 0,\n  \"idempotencyKey\": \"cmd_governed-spatial-agent-run_001\",\n  \"evidenceBoundary\": \"application-owned-workshop\"\n}"}],"simulationScenarios":[{"slug":"complete-journey","title":"Complete journey","outcome":"Commit the shortest reviewed success path to the journey-specific operating target.","href":"/tools/journey-lab?journey=governed-spatial-agent-run&scenario=complete-journey#lab"},{"slug":"idempotent-replay","title":"Idempotent replay","outcome":"Repeat one command identity and prove that version, event identity, and side effects do not duplicate.","href":"/tools/journey-lab?journey=governed-spatial-agent-run&scenario=idempotent-replay#lab"},{"slug":"stale-version","title":"Stale version","outcome":"Reject a command based on an outdated aggregate version without changing durable truth.","href":"/tools/journey-lab?journey=governed-spatial-agent-run&scenario=stale-version#lab"},{"slug":"invalid-transition","title":"Invalid transition","outcome":"Reject a known command when the current state does not permit it.","href":"/tools/journey-lab?journey=governed-spatial-agent-run&scenario=invalid-transition#lab"},{"slug":"unknown-outcome","title":"Unknown outcome recovery","outcome":"Reconcile after a lost response, then replay the original command identity safely.","href":"/tools/journey-lab?journey=governed-spatial-agent-run&scenario=unknown-outcome#lab"}],"acceptance":["All 6 reviewed transitions are implemented with actor and source-state checks.","All 6 application event identities are immutable and versioned.","Exact replay, idempotency conflict, stale version, invalid transition, and unknown outcome are tested.","Aggregate, event, audit, command result, and outbox intent commit atomically.","The Spatial Operations Agent capstone passes 15 declared tests after archive checksum verification.","Provider entitlement, payload, callback, completion, and production behavior remain independently evidenced."],"sourceBoundary":["The journey blueprint and application event contracts are implementation guidance, not Mappls provider payload specifications.","Only linked normalized contracts and source guides may define provider request syntax; empty evidence is never backfilled.","The simulator and maintained capstone operate in explicit fixture mode and make no entitlement claim.","Credentials, precise production payloads, opaque native objects, and provider secrets stay outside workshop inputs and durable examples."],"releaseBoundary":"Workshop completion proves an application-owned reliability design only. Production still requires issued entitlement, exact adapter contract tests, regional and quota validation, security/privacy review, operational drills, and independent release approval.","websitePath":"/journeys/governed-spatial-agent-run/workshop","apiPath":"/api/journey-workshops?journey=governed-spatial-agent-run","providerCalls":0,"writesExposed":false}