---
title: "The Three CASE Skills Services — How They Interlock"
description: "2026-09-29 · one repo (tc-case-inferral), one image (caseinferralworker), three queue types, three distinct jobs. This is the map of who produces what, who consumes it, and where the junctions are."
created: 2026-09-29
updated: 2026-09-29
authors: ThinkingCap R&D
topics: [Skills]
status: published
canonical: https://console.thinkingcap.com/rd/Skills/case-skills-services-interplay
---

# The Three CASE Skills Services — How They Interlock

*2026-09-29 · one repo (`tc-case-inferral`), one image (`caseinferralworker`), three
queue types, three distinct jobs. This is the map of who produces what, who consumes
it, and where the junctions are.*

```
                        CANONICAL FRAMEWORKS (global reference data)
                        ESDC SCT · ESCO · O*NET
                        skill_frameworks / skill_framework_entities
                        (+ legacy skill_taxonomy)
                          ▲                    ▲
              overlays / evidence              matches / "what is this activity ABOUT"
                          │                    │
 ┌────────────────────────┴───────┐   ┌────────┴───────────────────────────┐
 │ ② case-downward-inferral       │   │ ① case-inferral                    │
 │ org competency node            │   │ LMS activity (Course rows)         │
 │  → decompose DOWNWARD          │   │  → evidence packet → candidates    │
 │  → overlay mappings            │   │  → AI rerank                       │
 │  → terminal judgment           │   │  → inferral_results (per framework)│
 │  → RSD (the basement)          │   └────────────────────────────────────┘
 └───────────────┬────────────────┘
                 │ RSD is the junction
                 ▼
 ┌────────────────────────────────┐
 │ ③ case-experience-contract     │
 │ RSD → Experience Envelope      │
 │ (experience types, context /   │
 │  practice / evidence reqs,     │
 │  satisfaction tests = the door)│
 └────────────────────────────────┘
```

The chain in one sentence: **① tells you which canonical skills an activity is
about; ② decomposes the organization's own competency frameworks downward until
each terminal node becomes a Rich Skill Descriptor; ③ transforms an RSD into the
Experience Envelope a learning experience must satisfy.** ① and ② are
*independent* producers — they never call each other; they meet only on the
shared canonical frameworks. ③ depends on ②'s output. The eventual loop-closer
(P3) is attaching activities to RSDs — ①'s search machinery pointed at ②'s
basement.

---

## ① case-inferral — "what is this activity about?"

- **Service:** `service-case-inferral` (live) / `-dev` · queue `case_inferral`
  on **`patch_jobs`** (the fleet's original PG queue) — two moving parts:
  `jobCreation` enumerates clients + their active Course rows, hashes and
  enqueues; the analysis worker claims and processes.
- **Input:** LMS activities (all Course types) across clients.
- **Process:** AI evidence packet → DB candidate search (keyword FTS + vector
  when enabled + one-hop relationship expansion) → AI rerank, enum-constrained
  to retrieved candidates (can't invent ids).
- **Output:** `inferral_results` — matches per activity **per framework**
  (analyzed once, mapped to every enabled framework; the client picks a
  preferred framework in the UI without re-running).
- **Visible in:** skills surface, Tab 3 (external frameworks / inferral viewer).

## ② case-downward-inferral — "decompose our competencies toward actionable skills"

- **Service:** `service-case-downward-inferral` (live) / `-dev` · queue
  `case_downward_inferral` on **`maestro_queues`** (dedupe per
  tenant/framework/node/version via `dedupe_key` partial index — a repeat
  click while a job is live is a no-op).
- **Input:** the org's OWN frameworks (`org_frameworks`, schema/004) — authored,
  CASE-imported, or adopted from a canonical framework (adopted = imported +
  derivedFrom seed mappings at conf 1.0). Tenant = client, owner = branch.
- **Process per node:** overlay match into the canonical frameworks → propose
  children (downward-only acceptance — a child must sit strictly below) →
  per-child RSD judgment (SUBDIVIDE / RSD_CANDIDATE / UNRESOLVED) → SUBDIVIDE
  re-enqueues the child (depth+1), candidate materializes the descriptor.
- **Output:** `org_framework_nodes` (inferred children, confidence on
  everything), `org_node_mappings` (overlays with `reasoning_summary`),
  `org_framework_edges`, `downward_runs` (typed run projection),
  **`skill_rsd` at the terminal nodes (the basement — "we don't say candidate,
  we show the RSD")**, and — since schema/008 — `org_node_provenance`, the
  decision-time rationale for every inference and judgment.
- **Visible in:** skills surface, Tab 4 (Our Frameworks) — tree with inferred
  styling, overlay badges, Provenance shield, kill-switch reject cascades.

## ③ case-experience-contract — "what must an experience of this skill satisfy?"

- **Service:** `service-case-experience-contract` (live) / `-dev` · queue
  `case_experience_contract` on **`maestro_queues`** (dedupe pins the RSD
  artifact: `...:nodeId:rsdId:vVersion` — re-materializing the RSD re-arms the
  envelope, a repeat click on the same RSD is absorbed).
- **Input:** an RSD-terminal org node (`terminal_state='rsd'` + `rsd_id`) —
  **the envelope infers FROM the RSD, never from the bare node.** The tree and
  the basement are read-only inputs to this worker.
- **Process:** one contract per (node, RSD): experience types, context /
  practice / evidence requirements, coverage map, boundaries (declaring which
  ground belongs to child envelopes), advisory estimates, satisfaction tests.
- **Output:** `experience_contract` (one ACTIVE row per RSD; a regeneration
  supersedes its predecessor — nothing deleted) + `contract_runs` ledger.
- **Visible in:** Tab 4 — the envelope rides as a synthetic **child row** under
  its RSD node (Douglas: "rsd and ee never share the same card; envelope is
  always a child element") and opens its own card with Reject as its only
  review action.

---

## The junctions (what actually connects them)

1. **The canonical frameworks are shared evidence, not shared state.** ① maps
   activities *onto* them; ② uses them as the retrieval substrate for overlays
   and "what lies below" judgments. Neither writes to them (they're global
   reference data, loaded by `import:*`). If ① has never run for a client, ②
   is unaffected, and vice versa.
2. **The RSD is the hand-off.** ② is the only writer of `skill_rsd` for org
   nodes; ③ is the only reader-turned-producer. ③ never touches
   `org_framework_nodes` or `skill_rsd` rows.
3. **Version namespaces are deliberately decoupled** (Douglas, 2026-09-28:
   "each job touches the tree in its own way; they don't need to be connected
   in any way"). `downward_runs` versions ②'s work; `contract_runs` versions
   ③'s. An `ANALYSIS_VERSION` bump forces re-contracting within one service,
   never disturbs the other.
4. **One lifecycle everywhere:** matches and inferences are *provisional
   truth* (active + pending); humans are the **kill switch, not a gate**.
   Rejecting a mapping cascades to inferred descendants it solely supported;
   rejecting an inferred node supersedes its subtree; rejecting an envelope
   supersedes the contract row (which re-arms Infer Experience Envelope).
   **Nothing is ever deleted** — superseded rows are provenance.
5. **Provenance spans all three** (schema/008 + the surface's
   `skills.getOrgNodeProvenance`): every card's shield shows Sources +
   Rationale and walks the chain — canonical source → overlay mapping →
   inferred node → judgment → RSD → envelope. Nodes inferred before 008 say
   so honestly rather than fabricate a trail.

## Who talks to them (the surface and the queues)

| Concern | ① case-inferral | ② downward | ③ contract |
|---|---|---|---|
| Repo | `tc-case-inferral` (src/case, src/worker, src/jobCreation) | same repo (`src/downward`) | same repo (`src/contract`) |
| Image | `caseinferralworker` (+`-stable` dev) | same | same |
| Queue (table) | `case_inferral` (`patch_jobs`) | `case_downward_inferral` (`maestro_queues`) | `case_experience_contract` (`maestro_queues`) |
| Writes | `inferral_results` | org graph + `skill_rsd` + `org_node_provenance` | `experience_contract` |
| Run ledger | (job rows) | `downward_runs` | `contract_runs` |
| Enqueued by | `jobCreation` sweep | Tab 4: Infer Down / Run discovery / framework-enable | Tab 4: Infer Experience Envelope (+ `enqueue:contract` sweep per RSD) |
| Surface home | Tab 3 | Tab 4 (Our Frameworks) | Tab 4 (envelope child row) |

**Live-job greyness (2026-09-29):** the Tab 4 tree payload carries per-node
`inferDownQueued` / `envelopeQueued` read from live `maestro_queues` rows, so
the infer buttons grey out while a job holds the node and stay off once the
inference exists — re-arming means rejecting the inference first. The tree
self-refreshes every 8 s while anything is queued.

## Rollout state (as of 2026-09-29)

All three services are **enabled** in `worker_types` (live + disabled dev
pairs). Recent work awaiting rolls: provenance (schema/008 + worker writes,
srv/main `a5dd4d7` — needs a `caseinferralworker` image rebuild + reimage to
start recording) and the surface's provenance/grey-out/envelope-separation UI
(srv/main `ec183b6` — app/svc images tick-build automatically; roll via the
Releases card).
