§B2 · The consult itself, in person
The procedure, end to end
Eleven steps, in order. Who acts, what happens, what is recorded — and, marked as such, the steps where no software is involved at all.
Modality
In person, at the dispensary. No telemedicine. This is the § 5(a) design decision — “I will not do it via telemedicine” — and it is stricter than Pennsylvania law requires: § 1161a.25(a) would permit synchronous interaction. Being narrower than the statute is the point.
Actors: the cardholder (recipient — no account), the pharmacist (professional), the door’s budtender, a Rishi reviewer.
The eleven steps
offline marks a step with no software in it. Software does not reach these steps, and the honesty of where it does and does not reach is a feature of this document.
- 01
Walt arrives, card checked at the door by dispensary staff
System
dispensary's system only
What happens / what is recorded
Rishi is not in this step and has no visibility into it
- 02
Budtender routes him to the consult desk — either because the certifier did not specify form/dosage (§ 1181a.27(c)(7)(ii) makes the consult mandatory here), or because Walt asked
System
offline — no software
What happens / what is recorded
GAP-13: nothing in Rishi records why a consult was triggered, so the mandatory-vs-elective split — which is exactly what Marcus wants to audit — is not measurable
- 03
The door's system issues an opaque
consult_refSystem
dispensary's system
What happens / what is recorded
This is the entire identity handoff. The PHI stays on their side of the line
- 04
Priya opens the consult. She asks what else Walt takes before discussing any product
System
offline — no software
What happens / what is recorded
The conversion moment (A4). Also the clinically load-bearing one: warfarin, clobazam, tacrolimus, CNS depressants (memo 03 §4)
- 05
Protocol drives it. Priya works from the approved protocol for the condition cluster
System
protocolswherestatus='approved'What happens / what is recorded
A
draft/in_review/retiredprotocol may never be used — enforced downstream at step 9 with 422 - 06
Products are restricted to this door's shelf, fresh
System
GET /api/shelf-observations/recommend?locationId=…&conditionCode=…&maxAgeDays=14What happens / what is recorded
see below
- 07
Interaction screen runs
System
protocols.interaction_flags(jsonb) → read by a humanWhat happens / what is recorded
GAP-14. There is no interaction-screening engine.
interaction_flagsis content on the protocol;consult_records.interaction_flags_raisedis a jsonb array the caller supplies. Grep confirms no route computes, matches or validates a flag. The screen is Priya reading the protocol's flag list against what Walt tells her, and then typing what she raised. This is the single largest gap between the pitch and the software. - 08
Priya advises: form, starting dose, titration step, onset/duration, stop rules, what to do if nothing happens
System
offline — no software
What happens / what is recorded
from
protocols.titration_steps,route,onset_profile,contraindications - 09
Documentation written
System
POST /api/consult-records→consult_recordsWhat happens / what is recorded
fields:
consult_ref,protocol_id,protocol_version,condition_cluster,products_recommended(jsonb),interaction_flags_raised(jsonb),route_used,duration_min, pluslocation_id,professional_user_id, optionalshift_id. No patient column exists. Startsqa_status='pending' - 10
Priya may correct the record — only while pending
System
PATCH /api/consult-records/[id]What happens / what is recorded
rejected once reviewed. A reviewed consult is immutable
- 11
QA review
System
POST /api/consult-records/[id]/review→passed\What happens / what is recorded
flagged, withreviewed_by_id+reviewed_atfrom the session,qa_notes
Known limitations — stated, not buried
GAP-14 · blocks a pilot
There is no interaction-screening engine.
No interaction-screening engine. protocols.interaction_flags is content; consult_records.interaction_flags_raised is caller-supplied jsonb. Nothing computes, matches or validates a flag. The screen is a human reading a list
The screen is the pharmacist reading the protocol’s flag list against what the patient reports, and then typing what she raised. This is the single largest gap between the pitch and the software. Nothing computes, matches or validates a flag.
Journeys B2, C2 · the largest gap between the pitch and the software
GAP-13 · will be hit in the first month
Nothing records why a consult was triggered.
Nothing records why a consult was triggered — the mandatory § 1181a.27(c)(7)(ii) case vs elective. Exactly the split Marcus wants to audit
Journeys B2 · one column
-
GAP-15
POST /api/consult-recordsdoes not require an active coverage assignment or contract. The credential gate fires; the "was this door covered then" gate does not exist -
GAP-16
products_recommendedis unvalidated jsonb. Nothing checks the products recorded were on that door's fresh shelf — read path and write path are structurally disconnected -
GAP-17
No consult UI. Every route is an API; what Priya uses at the desk does not exist
What is missing from this journey — the source list, verbatim
- GAP-14 (above): no interaction-screening engine.
- GAP-15:
POST /api/consult-recordsdoes not require an activecoverage_assignmentor an activecoverage_contract. A consult can be recorded at a door outside any covered window. The credential gate fires; the "was this door actually covered at that moment" gate does not exist. - GAP-16:
products_recommendedis an unvalidated jsonb array. Nothing checks that the products recorded were actually on that door's fresh shelf — the recommend endpoint is a read path, structurally disconnected from the write path. A consult can record a product the recommender would have refused to surface. - GAP-17: there is no consult UI. Every route above is an API. What Priya actually uses at the desk does not exist.
What the patient IS told
- What form and what starting dose, and the titration step (Busse 2018's 1–2.5 mg THC increments framing).
- Onset and duration for that route, so he knows when to judge it.
- The interaction finding, named. "You're on warfarin. There is a Level 1 interaction signal and a published case of an INR at 7.2. I am not telling you not to do this; I am telling you that your INR needs checking, that your cardiologist needs to know, and that you do not change the dose between checks."
- The stop rules and what "not working" looks like.
- What the evidence actually says — small effect, high certainty for pain (Busse 2018, BMJ) — and that 15–19% of people in comparable cohorts do not respond at all (Allan 2018).
- Which of the products in front of him are on this shelf today, with measured cannabinoid values and the lot's test date and lab.
- That his name is not going into the pharmacist's company's records.
What the patient is NOT told
This list is the compliance surface. It is not trimmed.
- That cannabis treats his back pain, his surgery outcome, or anything else.
protocolscarries a first-classwhat_this_does_not_claimcolumn for this exact reason. - Any diagnosis. Priya does not diagnose. She works a condition cluster the certifier already named.
- Anything about certification or renewal. Barred by 28 Pa. Code § 1161a.25(e) and by the noncompete. Referral only (§B3).
- That a product is safe with warfarin. The honest statement is a flag and a referral, not a clearance.
- That it replaces his oxycodone, his gabapentin, or a conversation with his prescriber.
- A strain name or indica/sativa framing as though it carried clinical meaning.
- A causal or comparative claim about a brand. Nothing Sloane bought entitles anyone to say a word to Walt about it.
- Anything from a grade-D protocol (§B3).
What step 6 actually returns
GET /api/shelf-observations/recommend (app/api/shelf-observations/recommend/route.ts):
- 404
NO_APPROVED_PROTOCOLif the org has nostatus='approved'protocol for thatconditionCode. Ranking is never attempted. No approved protocol, no recommendation — full stop. - 404
LOCATION_NOT_FOUNDfor a cross-org door. results[]— products on this door'slocation_productswhoselast_seen_date >= cutoff(default 14 days) and whose latestshelf_observationsrow is notin_stock = false.stale[]— a separate array, never mixed into results (§B4).- Ranking:
ratioClassMatch(MEASUREDtotal_thc_pct/total_cbd_pctfrom the latestproduct_lotsrow, classified at a 2:1 dominance threshold) → thenformMatch(protocolroute→ productform) → then name, then id. Terpenes, "entourage", indica/sativa and strain name are never ranking variables and are returned tagged"claimed — manufacturer assertions; display-only, NEVER a ranking or matching variable"(memo 03 §5; Jikomes 2022). - A
claimRungstring on every response: "rung-2/3: … the product is ON THE SHELF at this door and lab-MEASURED … It does NOT say the product works for this condition."