{"schemaVersion":"mappls.journey-workshop.v1","slug":"realview-remote-inspection","journeySlug":"realview-remote-inspection","title":"Build Entitled RealView remote inspection","summary":"An eight-lab, source-bounded workshop for the complete remote visual inspection 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":"app-widgets-deep-links","stateModel":"stateful","aggregate":"remote visual inspection","actorCount":5,"stateCount":10,"transitionCount":11,"eventCount":11,"sourceGuideSlugs":["mappls-app-widgets","mappls-web-maps-js"],"contractSlugs":[],"relatedTutorialSlugs":[],"sample":{"slug":"realview-inspection-desk","name":"RealView Inspection Desk","downloadPath":"/downloads/realview-inspection-desk.zip","checksumPath":"/downloads/realview-inspection-desk.zip.sha256","verifiedTestCount":10,"runCommand":"npm test --workspace @mappls-example/realview-inspection-desk"},"labs":[{"slug":"model-lifecycle","title":"Model the lifecycle before the UI","duration":"15 min","objective":"Turn the remote visual inspection blueprint into an explicit aggregate boundary owned by the application.","build":["draft: The host owns an inspection purpose, external asset/site identity, Mappls Pin or coordinate, classification, and policy version.","entitlement_pending: Paid product access, exact host generation, browser credential class, restrictions, expiry, quota, and permitted use are being approved.","ready: The approved entitlement reference and bounded viewer configuration are current enough to open an attempt.","viewing: One browser lifecycle owns the iframe, exact origin, location/radius configuration, ephemeral token-handle reference, listener, timeout, and disposal.","coverage_unavailable: The exact Auth2 origin emitted the documented schema-valid status 204 for this attempt and configuration.","observation_recorded: An inspector saved a bounded human checklist and notes linked to the viewer attempt, without claiming provider metadata or copying imagery.","review_pending: An immutable observation set awaits a separately authorized reviewer under the declared purpose and policy.","accepted: A named reviewer accepted the observation set for the exact business decision and policy version.","rework_required: Review retained the prior observation and reason while requiring a new viewer attempt or alternate field evidence.","cancelled: An authorized actor ended the inspection with a bounded reason while preserving prior attempts and observations."],"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":["create_inspection by Host application: new aggregate → draft; emit realview_inspection.created.","request_entitlement by Inspector: draft → entitlement_pending; emit realview_inspection.entitlement_requested.","confirm_entitlement by Platform administrator: entitlement_pending → ready; emit realview_inspection.entitlement_confirmed.","open_viewer by Inspector: ready | coverage_unavailable | rework_required → viewing; emit realview_inspection.viewer_opened.","record_no_coverage by Host application: viewing → coverage_unavailable; emit realview_inspection.coverage_unavailable.","record_observation by Inspector: viewing → observation_recorded; emit realview_inspection.observation_recorded.","submit_review by Inspector: observation_recorded → review_pending; emit realview_inspection.review_requested.","accept by Inspection reviewer: review_pending → accepted; emit realview_inspection.accepted.","request_rework by Inspection reviewer: review_pending → rework_required; emit realview_inspection.rework_requested.","record_entitlement_expired by Host application: ready | viewing → entitlement_pending; emit realview_inspection.entitlement_expired.","cancel by Inspector: draft | entitlement_pending | ready | viewing | coverage_unavailable | observation_recorded | review_pending | rework_required → cancelled; emit realview_inspection.cancelled."],"prove":["create_inspection resolves to realview-remote-inspection-realview-inspection-created without claiming a provider webhook payload.","request_entitlement resolves to realview-remote-inspection-realview-inspection-entitlement-requested without claiming a provider webhook payload.","confirm_entitlement resolves to realview-remote-inspection-realview-inspection-entitlement-confirmed without claiming a provider webhook payload.","open_viewer resolves to realview-remote-inspection-realview-inspection-viewer-opened without claiming a provider webhook payload.","record_no_coverage resolves to realview-remote-inspection-realview-inspection-coverage-unavailable without claiming a provider webhook payload.","record_observation resolves to realview-remote-inspection-realview-inspection-observation-recorded without claiming a provider webhook payload.","submit_review resolves to realview-remote-inspection-realview-inspection-review-requested without claiming a provider webhook payload.","accept resolves to realview-remote-inspection-realview-inspection-accepted without claiming a provider webhook payload.","request_rework resolves to realview-remote-inspection-realview-inspection-rework-requested without claiming a provider webhook payload.","record_entitlement_expired resolves to realview-remote-inspection-realview-inspection-entitlement-expired without claiming a provider webhook payload.","cancel resolves to realview-remote-inspection-realview-inspection-cancelled 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":["Inspection aggregate: Current purpose, asset/site and location identity, ownership, policy, state, and optimistic version. Keys: inspectionId, externalId, assetId, mapplsPinOrCoordinate, purpose, state, version.","Entitlement reference: Non-secret proof of the approved product generation and credential policy. Keys: entitlementRef, product, host, environment, credentialClass, expiresAt, restrictionFingerprint.","Viewer attempt: Immutable browser lifecycle and documented no-imagery outcome. Keys: attemptId, configFingerprint, tokenHandleRef, openedAt, disposedAt, coverageStatus.","Observation and review: Human-authored evidence and separately authorized decision without copied imagery. Keys: observationId, attemptId, checklist, notes, observedAt, reviewer, decision, policyVersion.","Audit and outbox: Append-only transitions and exactly-once-in-effect downstream delivery. Keys: eventId, aggregateVersion, actor, idempotencyKey, outboxStatus."],"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":["No server secret, browser token value, or complete token-bearing iframe URL is persisted, logged, analyzed, exported, or sent to a model.","The exact selected origin and narrow documented schema are checked before any browser message reaches domain state.","Status 204 proves only no imagery for one attempt and configuration; HTTP 200 and frame load prove only a delivered shell.","Human observation is distinct from provider imagery, panorama metadata, measurement, currentness, and inspection acceptance.","Imagery is not copied, screenshotted, exported, or retained without an explicit licensed product contract and purpose-specific policy.","Every command is idempotent, compares expected version, and commits snapshot, audit, receipt, and outbox atomically.","Reviewer acceptance is attributable and cannot be performed by the same automated actor that created the observation."],"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":["Iframe returns a shell but no usable imagery: detect with HTTP/frame load occurred without entitled viewing or the documented 204 signal. Recover with Keep viewing unresolved until a bounded timeout, then record a host technical outcome rather than coverage or completion.","Message uses a lookalike origin or malformed payload: detect with Exact origin, object shape, field count, or integer status validation fails. Recover with Reject before domain processing, retain only a safe rejection metric, and never store the opaque payload.","Entitlement expires during viewing: detect with The entitlement reference is expired/revoked or the account-approved broker reports token expiry. Recover with Dispose the viewer, remove the ephemeral handle, return to entitlement pending, and retain the incomplete attempt.","Imagery is unavailable for the selected radius: detect with The active attempt receives the documented exact-origin status 204 message. Recover with Record configuration-specific no coverage and offer justified radius/location retry or alternate field evidence.","Reviewer cannot rely on the observation: detect with Checklist is incomplete, limitations are missing, purpose changed, or stronger field evidence is required. Recover with Request rework without overwriting the observation or prior viewer attempt.","Application restarts after an unknown command outcome: detect with Client lacks acknowledgement while aggregate, command key, and outbox state are durable. Recover with Reload and replay the same command key; never create another attempt or review decision to recover transport uncertainty."],"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 RealView Inspection Desk 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 (10 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":["Entitlement request age, confirmation, expiry, revocation, and renewal without token values","Viewer open, time-to-first-useful-state, timeout, disposal, and superseded-attempt count","Exact-origin/schema rejection counts without raw payload retention","Status 204 rate by bounded configuration fingerprint, not generalized geography","Observation completeness, limitation flags, and review duration","Rework reason and new-attempt conversion","Credential-bearing URL log/screenshot/model-context prevention checks","Idempotency replay, optimistic conflict, audit, outbox backlog, retry, and dead-letter age"],"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 = \"draft\" | \"entitlement_pending\" | \"ready\" | \"viewing\" | \"coverage_unavailable\" | \"observation_recorded\" | \"review_pending\" | \"accepted\" | \"rework_required\" | \"cancelled\";\ntype CommandName = \"create_inspection\" | \"request_entitlement\" | \"confirm_entitlement\" | \"open_viewer\" | \"record_no_coverage\" | \"record_observation\" | \"submit_review\" | \"accept\" | \"request_rework\" | \"record_entitlement_expired\" | \"cancel\";\n\ntype Command = {\n  name: CommandName;\n  aggregateId: string;\n  expectedVersion: number;\n  idempotencyKey: string;\n};\n\nconst transitions = {\n  \"create_inspection\": { from: [null], to: \"draft\", event: \"realview_inspection.created\" },\n  \"request_entitlement\": { from: [\"draft\"], to: \"entitlement_pending\", event: \"realview_inspection.entitlement_requested\" },\n  \"confirm_entitlement\": { from: [\"entitlement_pending\"], to: \"ready\", event: \"realview_inspection.entitlement_confirmed\" },\n  \"open_viewer\": { from: [\"ready\", \"coverage_unavailable\", \"rework_required\"], to: \"viewing\", event: \"realview_inspection.viewer_opened\" },\n  \"record_no_coverage\": { from: [\"viewing\"], to: \"coverage_unavailable\", event: \"realview_inspection.coverage_unavailable\" },\n  \"record_observation\": { from: [\"viewing\"], to: \"observation_recorded\", event: \"realview_inspection.observation_recorded\" },\n  \"submit_review\": { from: [\"observation_recorded\"], to: \"review_pending\", event: \"realview_inspection.review_requested\" },\n  \"accept\": { from: [\"review_pending\"], to: \"accepted\", event: \"realview_inspection.accepted\" },\n  \"request_rework\": { from: [\"review_pending\"], to: \"rework_required\", event: \"realview_inspection.rework_requested\" },\n  \"record_entitlement_expired\": { from: [\"ready\", \"viewing\"], to: \"entitlement_pending\", event: \"realview_inspection.entitlement_expired\" },\n  \"cancel\": { from: [\"draft\", \"entitlement_pending\", \"ready\", \"viewing\", \"coverage_unavailable\", \"observation_recorded\", \"review_pending\", \"rework_required\"], to: \"cancelled\", event: \"realview_inspection.cancelled\" },\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_realview_remote_inspection (\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_realview_remote_inspection_commands (\n  aggregate_id text NOT NULL REFERENCES journey_realview_remote_inspection(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_realview_remote_inspection_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\":\"realview-remote-inspection\",\"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\":\"realview-remote-inspection\",\"scenario\":\"unknown-outcome\"}'\n\n# Reconcile remotevisualinspection 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\": \"create_inspection\",\n  \"aggregateId\": \"fixture-realview-remote-inspection-001\",\n  \"expectedVersion\": 0,\n  \"idempotencyKey\": \"cmd_realview-remote-inspection_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=realview-remote-inspection&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=realview-remote-inspection&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=realview-remote-inspection&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=realview-remote-inspection&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=realview-remote-inspection&scenario=unknown-outcome#lab"}],"acceptance":["All 11 reviewed transitions are implemented with actor and source-state checks.","All 11 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 RealView Inspection Desk capstone passes 10 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/realview-remote-inspection/workshop","apiPath":"/api/journey-workshops?journey=realview-remote-inspection","providerCalls":0,"writesExposed":false}