{"schemaVersion":"mappls.journey-workshop.v1","slug":"vision-inference-review","journeySlug":"vision-inference-review","title":"Build Governed SkyDNN inference review","summary":"An eight-lab, source-bounded workshop for the complete vision evidence case 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":"vision evidence case","actorCount":6,"stateCount":8,"transitionCount":8,"eventCount":8,"sourceGuideSlugs":["skydnn-aiapi-docs"],"contractSlugs":["skydnn-ai-get-server-whoami-returns-the-current-models-in-port","skydnn-ai-get-models-api-model-key-returns-the-details-of-provided-model","skydnn-ai-post-predict-returns-the-prediction-as-response-in-form-of-json"],"relatedTutorialSlugs":[],"sample":{"slug":"vision-evidence-desk","name":"Vision Evidence Desk","downloadPath":"/downloads/vision-evidence-desk.zip","checksumPath":"/downloads/vision-evidence-desk.zip.sha256","verifiedTestCount":10,"runCommand":"npm test --workspace @mappls-example/vision-evidence-desk"},"labs":[{"slug":"model-lifecycle","title":"Model the lifecycle before the UI","duration":"15 min","objective":"Turn the vision evidence case blueprint into an explicit aggregate boundary owned by the application.","build":["registered: The application owns a purpose-bound case with opaque asset identity, SHA-256 content hash, media facts, capture time, location context, lawful-basis reference, and retention deadline.","model_locked: An entitled server-discovery result and one exact API model key, fingerprint, class vocabulary, and policy version are frozen for the case.","inference_requested: One worker owns an idempotent synchronous provider-call attempt against the exact asset and model identities.","inferred: A schema-valid provider response has normalized labels, confidence, bounded geometry, timing, provenance, and response hash without becoming a business decision.","review_pending: Versioned policy places the immutable result in an uncertainty, sensitive-class, or mandatory-sampling review lane.","accepted: An independent reviewer accepted the derived evidence for the declared purpose and exact policy/model/output versions.","rejected: An independent reviewer rejected the result with an attributable reason while retaining the immutable provider evidence.","redacted: Derived labels and geometry were removed at the retention deadline while the minimum decision, hashes, policy, and audit record remain."],"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":["register_case by Asset steward: new aggregate → registered; emit vision.case_registered.","lock_model by Vision worker: registered → model_locked; emit vision.model_locked.","request_inference by Vision worker: model_locked → inference_requested; emit vision.inference_requested.","record_inference by Vision worker: inference_requested → inferred; emit vision.inference_recorded.","submit_review by Policy service: inferred → review_pending; emit vision.review_requested.","accept by Independent reviewer: review_pending → accepted; emit vision.accepted.","reject by Independent reviewer: review_pending → rejected; emit vision.rejected.","redact by Privacy worker: inferred | review_pending | accepted | rejected → redacted; emit vision.redacted."],"prove":["register_case resolves to vision-inference-review-vision-case-registered without claiming a provider webhook payload.","lock_model resolves to vision-inference-review-vision-model-locked without claiming a provider webhook payload.","request_inference resolves to vision-inference-review-vision-inference-requested without claiming a provider webhook payload.","record_inference resolves to vision-inference-review-vision-inference-recorded without claiming a provider webhook payload.","submit_review resolves to vision-inference-review-vision-review-requested without claiming a provider webhook payload.","accept resolves to vision-inference-review-vision-accepted without claiming a provider webhook payload.","reject resolves to vision-inference-review-vision-rejected without claiming a provider webhook payload.","redact resolves to vision-inference-review-vision-redacted 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":["Vision case: Business purpose, external identity, lifecycle, policy, retention, and optimistic version. Keys: caseId, externalId, purpose, state, version, policyVersion, retentionUntil.","Asset envelope: Opaque, non-media identity and integrity facts for one captured source. Keys: assetRef, sha256, mediaClass, width, height, capturedAt, lawfulBasisRef.","Model lock: Exact entitled discovery evidence and immutable model selection. Keys: serverRef, apiModelKey, modelFingerprint, classes, lockedAt.","Inference attempt: Synchronous request lifecycle, normalized response, safe failure, timing, and provenance. Keys: attemptId, assetHash, modelFingerprint, status, resultHash, regions, timings.","Review and retention: Policy reasoning, attributable disposition, and derived-detail redaction evidence. Keys: reviewId, reasonCodes, reviewer, decision, redactedAt.","Audit and outbox: Append-only state evidence and exactly-once-in-effect downstream notification. Keys: eventId, aggregateVersion, idempotencyKey, actor, 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 image bytes, media URL, bearer token, credential, face, number plate, or provider-internal file path enters the reference case store.","The provider POST /predict call is synchronous; application queue, review, retry, decision, and retention states are never attributed to SkyDNN.","Every attempt binds an immutable asset hash to one discovered model fingerprint and policy version.","Confidence and geometry are evidence, not a business outcome or live safety command.","Reviewer identity is independent from the fixture inference actor and every override or rejection has a reason.","Idempotency, optimistic versions, audit, receipts, and outbox commit atomically."],"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":["Model discovery changes after the case is prepared: detect with Current metadata fingerprint differs from the locked fingerprint. Recover with Stop, create a new model lock and attempt, and retain the original lock for comparison.","Provider response is lost after a synchronous request: detect with The active attempt has no terminal response hash and transport outcome is ambiguous. Recover with Reconcile with the approved provider boundary when possible or record a safe failed attempt before a deliberate linked retry.","Geometry, label, or confidence is malformed: detect with Schema, bounds, topology, vocabulary, or numeric validation fails. Recover with Quarantine derived detail, retain a safe error class, and do not send it to policy or review.","Sensitive or uncertain inference reaches policy: detect with A privacy class, low confidence, unsupported label, or mandatory sample rule matches. Recover with Require independent review and prevent automation from issuing a terminal business or safety action.","Retention deadline passes while review is open: detect with Derived output remains present after the committed policy deadline. Recover with Redact on schedule, close or fail the review with explicit expiry, and retain minimum audit evidence.","Process restarts after a command timeout: detect with The client lacks acknowledgement while snapshot, receipt, audit, and outbox are durable. Recover with Reload and replay the same idempotency key without duplicating attempts, decisions, or events."],"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 Vision Evidence 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":["Cases by state, purpose, media class, model fingerprint, and policy version","Model discovery drift, lock age, and incompatible input rate","Inference latency, safe error class, result-schema rejection, and ambiguous outcome","Confidence, label, geometry type, and review-reason distributions","Review queue age, disposition, override, disagreement, and sampling coverage","Sensitive-class access and derived-output retention/redaction deadlines","Idempotency replay, version conflict, restart recovery, audit, and outbox 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 = \"registered\" | \"model_locked\" | \"inference_requested\" | \"inferred\" | \"review_pending\" | \"accepted\" | \"rejected\" | \"redacted\";\ntype CommandName = \"register_case\" | \"lock_model\" | \"request_inference\" | \"record_inference\" | \"submit_review\" | \"accept\" | \"reject\" | \"redact\";\n\ntype Command = {\n  name: CommandName;\n  aggregateId: string;\n  expectedVersion: number;\n  idempotencyKey: string;\n};\n\nconst transitions = {\n  \"register_case\": { from: [null], to: \"registered\", event: \"vision.case_registered\" },\n  \"lock_model\": { from: [\"registered\"], to: \"model_locked\", event: \"vision.model_locked\" },\n  \"request_inference\": { from: [\"model_locked\"], to: \"inference_requested\", event: \"vision.inference_requested\" },\n  \"record_inference\": { from: [\"inference_requested\"], to: \"inferred\", event: \"vision.inference_recorded\" },\n  \"submit_review\": { from: [\"inferred\"], to: \"review_pending\", event: \"vision.review_requested\" },\n  \"accept\": { from: [\"review_pending\"], to: \"accepted\", event: \"vision.accepted\" },\n  \"reject\": { from: [\"review_pending\"], to: \"rejected\", event: \"vision.rejected\" },\n  \"redact\": { from: [\"inferred\", \"review_pending\", \"accepted\", \"rejected\"], to: \"redacted\", event: \"vision.redacted\" },\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_vision_inference_review (\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_vision_inference_review_commands (\n  aggregate_id text NOT NULL REFERENCES journey_vision_inference_review(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_vision_inference_review_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\":\"vision-inference-review\",\"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\":\"vision-inference-review\",\"scenario\":\"unknown-outcome\"}'\n\n# Reconcile visionevidencecase 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\": \"register_case\",\n  \"aggregateId\": \"fixture-vision-inference-review-001\",\n  \"expectedVersion\": 0,\n  \"idempotencyKey\": \"cmd_vision-inference-review_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=vision-inference-review&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=vision-inference-review&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=vision-inference-review&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=vision-inference-review&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=vision-inference-review&scenario=unknown-outcome#lab"}],"acceptance":["All 8 reviewed transitions are implemented with actor and source-state checks.","All 8 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 Vision Evidence 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/vision-inference-review/workshop","apiPath":"/api/journey-workshops?journey=vision-inference-review","providerCalls":0,"writesExposed":false}