@crvouga/mockingbird-service-junction 3.6.1 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog — @crvouga/mockingbird-service-junction
2
2
 
3
+ ## 3.7.1 (2026-10-20)
4
+
5
+ ### Fixes and improvements
6
+
7
+ - resolve issues without an oracle gate ([96e84be](https://github.com/crvouga/mockingbird/commit/96e84be3964a0baa5d8d82c59339d4bbc264b1a6))
8
+
9
+ ## 3.7.0 (2026-10-18)
10
+
11
+ ### Features
12
+
13
+ - resolve the agent-reported issue queue in one batch ([25c52b6](https://github.com/crvouga/mockingbird/commit/25c52b668a53ddb80b968419601a20abf33f956b))
14
+
15
+ ### Fixes and improvements
16
+
17
+ - record reported production refusal for delegated explicit client_bill ([3525699](https://github.com/crvouga/mockingbird/commit/35256996166f4f8ddcf25f16b7fcca2ca72c1a38))
18
+
3
19
  ## 3.6.1 (2026-10-18)
4
20
 
5
21
  ### Fixes and improvements
package/README.md CHANGED
@@ -56,7 +56,11 @@ npx mockingbird-junction serve --help
56
56
  | `--port <n>` | `8787` | Port to listen on (`0` for any free port; the bound URL is printed) |
57
57
  | `--host <host>` | `127.0.0.1` | Interface to bind |
58
58
  | `--corpus <default\|none\|file>` | `default` | The shipped corpus, no corpus (synthetic data), or a file from `corpus pull` |
59
- | `--geo <corpus\|synthetic>` | `corpus` with a corpus | How unknown ZIPs are answered; see [Geo](#geo-corpus-or-synthetic) |
59
+ | `--geo <corpus\|synthetic\|corpus+synthetic>` | `corpus` with a corpus | How unknown ZIPs are answered; see [Geo](#geo-corpus-or-synthetic) |
60
+ | `--zip-overlays <file>` | — | [ZIP aliases and per-ZIP overrides](#hybrid-geo-and-zip-overlays) |
61
+ | `--catalog-overlays <file>` | — | [Lab tests derived from catalog ones](#catalog-overlays-and-a-la-carte-markers) at boot |
62
+ | `--marker-catalog <file>` | — | Markers `GET /v3/lab_tests/markers` offers, with [pricing](#catalog-overlays-and-a-la-carte-markers) |
63
+ | `--add-on-source-markers` | off | Name the markers behind each add-on result line |
60
64
  | `--lab-accounts <file>` | built-in fixtures | [Lab accounts](#lab-accounts): a JSON array, or `{ "presets": [...], "accounts": [...] }` |
61
65
  | `--team-id <uuid>` | the corpus's team | The [team](#team-identity) the emulator answers as |
62
66
  | `--max-users <n>` | unlimited | Enforce the sandbox's [live-user cap](#sandbox-limits) |
@@ -245,10 +249,11 @@ Every Mockingbird service answers the same control surface, outside the vendor's
245
249
  | `GET` / `POST` / `DELETE /__admin/faults` | List, add or clear [fault rules](#faults-and-error-shapes) (`DELETE ?id=` removes one) |
246
250
  | `POST /__admin/faults/presets/{name}` | Add a named Junction fault; body may override `count`, `rate`, `operationId`… |
247
251
  | `GET /__admin/metrics` | Requests by operation and status, fault count, and **unmatched routes** |
248
- | `GET /__admin/orders` | Orders in the namespace with their current status and the `lab_account_id` each was placed with (`null`: Junction's platform account) |
252
+ | `GET /__admin/orders` | Orders in the namespace with their current status, the `lab_account_id` each was placed with (`null`: Junction's platform account) and `lab_account_request` (below) |
249
253
  | `POST /__admin/orders/{id}/transition` | Move an order: `{ "to": "completed", "result": "abnormal" }` ([order control](#order-control)) |
250
254
  | `GET` / `PUT /__admin/results/{id}` | Read or install an exact result payload / PDF for an order |
251
255
  | `GET /__admin/result-fixtures` | The named results |
256
+ | `GET` / `PUT /__admin/quirks` | Read or replace the namespace's opt-in [sandbox quirks](#sandbox-quirks) (`{ "quirks": { "sameSecondCancelStatus": true } }`) |
252
257
  | `GET` / `PUT /__admin/lab-accounts` | Read or replace the namespace's [lab accounts](#lab-accounts) (`{ "accounts": [...], "presets": [...] }`; `{ "accounts": null }` restores the default) |
253
258
  | `POST /__admin/lab-accounts` | Add one account; 409 if the id exists |
254
259
  | `PATCH` / `DELETE /__admin/lab-accounts/{id}` | Merge fields into an account (`{ "status": "suspended" }`, `{ "states": [...] }`) / remove it; 404 if absent |
@@ -328,6 +333,92 @@ a radius with no recording, or any read with parameters the corpus did not recor
328
333
  `--geo synthetic` the derivation still applies, and only reads with nothing to derive from are
329
334
  invented.
330
335
 
336
+ ### Hybrid geo and ZIP overlays
337
+
338
+ `--geo corpus+synthetic` (`geo: "corpus+synthetic"`) replays the recording for the ZIPs the corpus
339
+ covers and synthesizes every other ZIP, so a suite needs no second, synthetic runtime as a
340
+ fallback. A covered ZIP is still never answered next to its recording: a read the recording cannot
341
+ answer (see above) refuses with the same 424 as `--geo corpus`. Switch it per namespace with
342
+ `PUT /__admin/geo {"mode": "corpus+synthetic"}`.
343
+
344
+ `zipOverlays` (`--zip-overlays <file>`, or `PUT /__admin/geo {"overlays": {...}}`) edits answers
345
+ for individual ZIPs, in any geo mode:
346
+
347
+ ```json
348
+ {
349
+ "aliases": { "07105": "85004" },
350
+ "overrides": { "07105": { "phlebotomy_served": true }, "10118": { "no_pscs": true } }
351
+ }
352
+ ```
353
+
354
+ An alias makes a ZIP answer as another one (coverage, recordings and the synthetic model all use
355
+ the target; the response echoes the requested ZIP). `phlebotomy_served` sets
356
+ `phlebotomy.is_served` (and `providers`) on `area/info`; `no_pscs` empties the `psc/info` list and
357
+ zeroes the `within_radius` counts on `area/info`. An override is looked up by the requested ZIP,
358
+ then by its alias target. Overlays are configuration: they survive `reset()`.
359
+
360
+ The sealed sandbox recording agrees with the report behind this feature that NJ `07030` is not
361
+ served for at-home phlebotomy, and the synthetic model reports `10118`, `11201`, `12207`, `07030`
362
+ and `07601` as not served. It does not know that NJ `07105` is served (the synthetic model only
363
+ serves a handful of ZIP prefixes), so set that one with an override.
364
+
365
+ ## Catalog overlays and a-la-carte markers
366
+
367
+ **Catalog overlays** (`catalogOverlays`, `--catalog-overlays <file>`) add lab tests derived from
368
+ ones the catalog already holds, with no network access, to every namespace at boot and again on
369
+ every `reset()`:
370
+
371
+ ```ts
372
+ import { createRuntime } from "@crvouga/mockingbird-service-junction"
373
+
374
+ createRuntime({
375
+ catalogOverlays: [
376
+ {
377
+ from: "lipid_panel_athome", // id or slug of a catalog lab test (or an earlier overlay)
378
+ id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
379
+ name: "Lipid Panel: Quest",
380
+ slug: "lipid_panel_quest", // default: derived from name
381
+ lab: "sonora_quest", // a lab slug the catalog lists, or a lab record { id, slug, name, ... }
382
+ stripAoe: true, // markers lose their ask-on-order questions
383
+ autoGenerated: true, // lists with generation_method=auto, not by default
384
+ },
385
+ ],
386
+ })
387
+ ```
388
+
389
+ `method`, `price` (dollars) and `status` can be overridden too. Moving the clone to a lab gives its
390
+ markers and expected results that lab's id. A bad overlay (unknown `from`, non-UUID or duplicate
391
+ `id`, unknown `lab`) fails at boot naming `catalogOverlays[n].<field>`.
392
+
393
+ **A-la-carte markers.** `GET /v3/lab_tests/markers` lists the markers a team can order one by one:
394
+ those embedded in active catalog lab tests, plus `markerCatalog` entries (`--marker-catalog
395
+ <file>`) with their own `price`, `pricing`, `aoe`, `expected_results` and `a_la_carte_enabled`. It
396
+ takes `lab_id`, `lab_slug` (wins over `lab_id`), `name` (a name substring, or a provider test code),
397
+ `a_la_carte_enabled`, `lab_account_id` (narrows to the account's lab), `include_pricing`, `page` and
398
+ `size`, and answers `{ markers, total, page, size, pages }` (plus `pricing: { currency, data }` keyed
399
+ by marker id for the page's markers with `include_pricing=true`). A marker price is read as dollars
400
+ (`"12.50"` is `base_amount_minor: 1250`); `"N/A"` is `{ "unspecified": "price_unavailable" }`.
401
+
402
+ `POST /v3/lab_test/estimate_order_set_pricing` prices each order set (`lab_test_ids` and/or
403
+ `add_on.marker_ids` / `provider_ids`) as one `lab_charge` component: the sum of the lab tests'
404
+ `price` and the add-on markers' prices, with a `marker_breakdown` for add-ons. If any add-on marker
405
+ has no stated price the whole estimate is `price_unavailable`. `modality`, `us_state` and `billing`
406
+ are validated but do not change the price, and no other charge components are modelled.
407
+
408
+ Both routes follow the published contract
409
+ ([markers](https://docs.junction.com/api-reference/lab-testing/biomarkers),
410
+ [estimate](https://docs.junction.com/api-reference/lab-testing/estimate-order-set-pricing)); neither
411
+ has been compared with the live sandbox (prices are contract-owned), so their parity is disabled and
412
+ the 400 messages for unknown tests/markers are borrowed from `create_order`.
413
+
414
+ `addOnSourceMarkers: true` (`--add-on-source-markers`) makes each biomarker line of an add-on order
415
+ carry `source_markers: [{ marker_id, name, slug, provider_id }]`. It is off by default, because the
416
+ default result lines carry no `source_markers`, and was reported from a sandbox probe that has not
417
+ been re-verified.
418
+
419
+ `GET /v3/lab_tests` lists every lab test unless filtered; pass `status=active` to exclude inactive
420
+ ones. The documented default of that filter is not stated, so the emulator does not change it.
421
+
331
422
  ## Lab accounts
332
423
 
333
424
  `create_order` selects explicit linked accounts in either `active` or `ready_to_launch`
@@ -445,8 +536,21 @@ but NJ, NY and RI (47).
445
536
  | `bioreference_platform` | `bioreference` | `not_delegated` | `client_bill`: `PLATFORM_ACCOUNT_STATES` | `[]` | recorded ("Junction BioReference Account") |
446
537
  | `bioreference_patient_bill_passthrough` | `bioreference` | `not_delegated` | `patient_bill_passthrough`: NJ, NY | the team | recorded |
447
538
  | `quest_platform` / `labcorp_platform` | `quest` / `labcorp` | `not_delegated` | `client_bill`: `PLATFORM_ACCOUNT_STATES` | `[]` | recorded ("Junction Quest/Labcorp Account") |
539
+ | `bioreference_current_platform` | `bioreference` | `not_delegated` | `patient_bill_passthrough`: NJ, NY | `[]` | team listing reported 2026-10-18 (#475) |
540
+ | `quest_sandbox_platform` | `quest` | `not_delegated` | `client_bill`: `PLATFORM_ACCOUNT_STATES`; `commercial_insurance`: all 50 | `[]` | sandbox listing reported 2026-10-18 (#475) |
541
+ | `sonora_quest_production_platform` | `sonora_quest` | `not_delegated` | `client_bill`: `PLATFORM_ACCOUNT_STATES` | `[]` | production listing reported 2026-10-18 (#475) |
448
542
  | `suspended_bioreference` / `suspended_quest` / `suspended_labcorp` | as above | `not_delegated` | as the platform accounts | `[]` | the platform shapes with `status: "suspended"` |
449
543
 
544
+ The September recording's `bioreference_platform` is **illustrative, not a current listing**: the
545
+ house BioReference account now lists only `patient_bill_passthrough` for NJ and NY (use
546
+ `bioreference_current_platform`, and see
547
+ [omitted house-account billing](#omitted-house-account-billing-467) for how production still
548
+ accepts `client_bill` when no account is named). No account at any lab lists DC, so no preset does
549
+ either. `bioreference_current_platform`, `quest_sandbox_platform` and `sonora_quest_production_platform`
550
+ follow listings an agent
551
+ reported from `GET /v3/lab_test/lab_account` (a consumer-audit report with synthetic ids, not a
552
+ repository probe); the older presets keep their shapes so pinned suites do not change.
553
+
450
554
  Every preset is `status: "active"` unless named `suspended_*`. `GET /__admin/lab-accounts/presets`
451
555
  returns the full records, the deprecated `aliases`, and the `synthetic` presets (not a recorded
452
556
  account). `bioreference_customer_multi_state` is a deprecated alias of `bioreference_platform`: it
@@ -584,6 +688,8 @@ valid choices, never approximated. `result` picks a named result — `normal`, `
584
688
  shape. For an exact payload, `PUT /__admin/results/{id}` with `{ "results": [...],
585
689
  "missing_results": null, "interpretation": "critical", "pdf_base64": "…" }`.
586
690
 
691
+ **Same-second cancel.** See the opt-in [sandbox quirks](#sandbox-quirks).
692
+
587
693
  There is no `isTestCancelled` in Junction's contract; cancelling produces the vendor's
588
694
  `cancelled.<method>.cancelled` event and webhook, which is what a consumer derives that flag from.
589
695
 
@@ -630,6 +736,23 @@ so one worker's injected failure never lands on another's request; one added in-
630
736
  applies to every namespace unless given a `namespace`. Over HTTP: `POST /__admin/faults` with the same fields, or
631
737
  `POST /__admin/faults/presets/sandbox_user_quota`.
632
738
 
739
+ A rule can also match on the request itself: `path`, `pathPattern`, `requestHeaders`, `query`, and
740
+ `requestBody`, a map from a dotted path in the JSON body to the value it must equal. That targets
741
+ one member when the backend sends no namespace header, with an optional `body` to answer with:
742
+
743
+ ```bash
744
+ # Refuse one member's order
745
+ curl -X POST localhost:8787/__admin/faults -d '{"method":"POST","path":"/v3/order",
746
+ "requestBody":{"patient_details.email":"member-a@example.test"},"status":503}'
747
+ # No phlebotomy slots for one ZIP code
748
+ curl -X POST localhost:8787/__admin/faults -d '{"method":"POST",
749
+ "pathPrefix":"/v3/order/phlebotomy/appointment/availability",
750
+ "requestBody":{"zip_code":"00000"},"status":200,"body":{"slots":[],"timezone":"America/Phoenix"}}'
751
+ ```
752
+
753
+ A body that is missing, not JSON, or not a JSON object never matches. See the
754
+ [fault rule reference](../../../docs/admin-controls/FAULTS.md#match-on-the-request-body).
755
+
633
756
  ## Webhooks
634
757
 
635
758
  With `webhooks` / `--webhook-url`, every event is delivered signed, with retries:
@@ -730,7 +853,7 @@ Main entry (`@crvouga/mockingbird-service-junction`, runtime-neutral):
730
853
 
731
854
  | Export | Description |
732
855
  | --- | --- |
733
- | `createRuntime` | `(options?: JunctionRuntimeOptions) => JunctionRuntime` — the served emulator (health, admin, namespaces, clock, faults, metrics, journal, webhooks) as one `fetch`. Options: `corpus`, `geo`, `labAccounts`, `teamId`, `limits`, `identity`, `defaultBillingTypes`, `fixtures`, `journalSize`, `webhooks`, `onWebhook`, `sqlite`, `clock`, `seed`, `adminKey`, `onLog`. |
856
+ | `createRuntime` | `(options?: JunctionRuntimeOptions) => JunctionRuntime` — the served emulator (health, admin, namespaces, clock, faults, metrics, journal, webhooks) as one `fetch`. Options: `corpus`, `geo`, `zipOverlays`, `catalogOverlays`, `markerCatalog`, `addOnSourceMarkers`, `labAccounts`, `teamId`, `limits`, `identity`, `defaultBillingTypes`, `labAccountBehavior`, `quirks`, `fixtures`, `journalSize`, `webhooks`, `onWebhook`, `sqlite`, `clock`, `seed`, `adminKey`, `onLog`. |
734
857
  | `JunctionAPI` | Class. `new JunctionAPI(options?)`: one namespace's emulator, implementing `fetch(request: Request): Promise<Response>`. |
735
858
  | `JUNCTION_NAMESPACE` | `"junction"` — the default namespace's storage key when sharing a `sqlite` client. |
736
859
  | `document` | The vendored Junction OpenAPI document (Mockingbird subset) that drives routing. |
@@ -801,7 +924,8 @@ Main entry (`@crvouga/mockingbird-service-junction`, runtime-neutral):
801
924
  | `reset()` | `Promise<void>` — clear state; re-install the corpus or default catalog, and the configured lab accounts. |
802
925
  | `installCorpus(corpus)` | Install a `SealedCorpus`: observations (shared, read-only), catalog, labs, lab accounts, ZIP coverage. |
803
926
  | `corpusInfo()` | `CorpusInfo \| undefined` — label, recording date, source and counts. |
804
- | `geoMode` | `"corpus" \| "synthetic"`, settable. |
927
+ | `geoMode` | `"corpus" \| "synthetic" \| "corpus+synthetic"`, settable. |
928
+ | `zipOverlays` | [ZIP aliases and overrides](#hybrid-geo-and-zip-overlays) for this namespace; settable, kept across `reset()`. |
805
929
  | `configureLabAccounts(layout?)` / `labAccounts()` | Replace (a list, or `{ presets, accounts }`; `undefined` restores the default) / read the team's lab accounts. |
806
930
  | `addLabAccount(input)` / `addLabAccountPreset(name, id?)` / `patchLabAccount(id, patch)` / `removeLabAccount(id)` | Change one account; kept across `reset()`. |
807
931
  | `teamId` | The team this namespace answers as. |
@@ -828,7 +952,11 @@ type JunctionAPIOptions = {
828
952
  namespace?: string // storage namespace; default "junction"
829
953
  now?: () => number // clock in ms; default Date.now
830
954
  corpus?: SealedCorpus // installed now and on every reset()
831
- geo?: "corpus" | "synthetic" // default "corpus" with a corpus, else "synthetic"
955
+ geo?: "corpus" | "synthetic" | "corpus+synthetic" // default "corpus" with a corpus, else "synthetic"
956
+ zipOverlays?: { aliases?: Record<string, string>; overrides?: Record<string, { phlebotomy_served?: boolean; no_pscs?: boolean }> }
957
+ catalogOverlays?: CatalogOverlay[] // lab tests cloned from catalog ones, now and on every reset()
958
+ markerCatalog?: MarkerCatalogEntry[] // markers GET /v3/lab_tests/markers offers on top of the catalog's
959
+ addOnSourceMarkers?: boolean // add-on result lines carry source_markers; default false
832
960
  labAccounts?: LabAccountInput[] | { presets?: (string | { name; id? })[]; accounts?: LabAccountInput[] }
833
961
  teamId?: string // default: the corpus's recorded team, else MOCK_TEAM_ID
834
962
  limits?: { maxUsers?: number | null; simulateRequiresSandbox?: boolean; rateLimitPerSecond?: number | null }
@@ -921,29 +1049,71 @@ from the public contract, **not measured production parity**. Both `expected-con
921
1049
  and `sandbox-observed` return the persisted account association in POST/GET; a synthetic
922
1050
  requisition uses that same stored provider account number.
923
1051
 
924
- `sandbox-observed` uses explicit, account-owned observations, never a global delegated
925
- account billing override. For example, with synthetic fixture IDs:
1052
+ For [#440](https://github.com/crvouga/mockingbird/issues/440), the scoped account-owned
1053
+ billing control is a **best guess, not oracle-verified**. The reporter observed an explicit
1054
+ `client_bill` refusal for a root-selected delegated BioReference account even though its listing
1055
+ allowed that billing in AZ. They reported `400 {"detail":"Lab 13 does not support billing type patient_bill_passthrough"}`
1056
+ in the sandbox and the same error with `Lab 14` in production, including sandbox panel/add-on,
1057
+ home/walk-in and physician-present controls. These are report-derived errors; neither their
1058
+ cause nor a general vendor billing rule has been established. The repository's
1059
+ [2026-10-20 sanitized probe](https://github.com/crvouga/mockingbird/actions/runs/39397939244)
1060
+ could not find matching account/test inventory, recorded no order observations and created no
1061
+ user or orders. This evidence limit does not prevent a suite from testing the explicit fallback.
1062
+
1063
+ Configure the reported refusal against the ID of a synthetic account loaded by `labAccounts`
1064
+ or your corpus. For example:
926
1065
 
927
1066
  ```ts
928
1067
  import { createRuntime } from "@crvouga/mockingbird-service-junction"
929
1068
 
1069
+ const delegatedAccountId = "11111111-1111-4111-8111-111111111111"
930
1070
  const runtime = createRuntime({
1071
+ labAccounts: [{
1072
+ id: delegatedAccountId,
1073
+ lab: "bioreference",
1074
+ org_id: "55555555-5555-4555-8555-555555555555",
1075
+ delegated_flow: "order_delegated",
1076
+ allowed_billing: { client_bill: ["AZ"] },
1077
+ }],
931
1078
  labAccountBehavior: {
932
- profile: "sandbox-observed",
1079
+ profile: "observed",
1080
+ oracle: "sandbox",
933
1081
  accounts: {
934
- "synthetic-delegated-id": {
1082
+ [delegatedAccountId]: {
935
1083
  rejectBilling: { client_bill: "Lab 13 does not support billing type patient_bill_passthrough" },
936
1084
  },
937
- "synthetic-ny-id": { allowBilling: { client_bill: ["NY", "NJ"] } },
938
1085
  },
939
1086
  },
940
1087
  })
941
1088
  ```
942
1089
 
943
- These controls encode the independently reported sandbox observations in #440/#441;
944
- they do not establish production billing behavior. Exceptions apply only to explicit
945
- root account IDs and explicit billing. `defaultBillingTypes` still governs omitted
946
- billing independently. `GET/PUT /__admin/lab-account-behavior` inspect/change one
1090
+ Load a BioReference panel or marker catalog in the same runtime's corpus, then submit your
1091
+ normal valid order with root `lab_account_id: delegatedAccountId` and
1092
+ `billing_type: "client_bill"`. This configuration returns the exact configured 400 detail before
1093
+ creating an order or webhook, including when `physician` is present. It applies to both panel
1094
+ and add-on orders and home/walk-in collection. To model the production report, choose
1095
+ `oracle: "production"` and change the configured detail to `Lab 14 ...`; the oracle label
1096
+ describes the intended environment, not proof of verification. `sandbox-observed` remains an
1097
+ alias for `observed` with `oracle: "sandbox"`.
1098
+
1099
+ The fallback is deliberately narrow: only that listed account's explicit root ID and explicit
1100
+ billing activate `rejectBilling`. Other delegated accounts keep their own policies. Omitting
1101
+ `billing_type` still uses `defaultBillingTypes` (or `client_bill` when no default is configured)
1102
+ and checks the account's listed billing. Thus this fixture accepts an omitted default
1103
+ `client_bill` even though it refuses explicit `client_bill`; configuring an omitted
1104
+ `patient_bill_passthrough` default rejects because the account lacks that billing, independently
1105
+ of `rejectBilling`. This distinction is the chosen emulator contract, not a verified production
1106
+ omission behavior. Nested-only IDs do not select the customer account. In the sandbox profile
1107
+ they follow the platform fallback with null POST/GET association; a successful root-selected
1108
+ account retains its persisted ID on both responses. Never treat nested-only acceptance as a
1109
+ successful delegated-account selection or retry a rejected customer order by dropping its ID.
1110
+
1111
+ These controls do not model why Junction overrides the requested billing, validate physician
1112
+ selection, or establish any production omission/returned-identity rule. Native behavior remains
1113
+ the default. For the separately reported NY/NJ exception in #441, `allowBilling` can map an
1114
+ explicit billing type to allowed states on that fixture's account, for example
1115
+ `{ allowBilling: { client_bill: ["NY", "NJ"] } }`; this too is a best guess, not oracle-verified.
1116
+ `GET/PUT /__admin/lab-account-behavior` inspect/change one
947
1117
  namespace's policy; changes survive its reset and never affect another namespace.
948
1118
  Unknown profile names are rejected. CLI/server equivalents are `--lab-account-profile`
949
1119
  and `--lab-account-sandbox-policies <json-file>` (the account-policy object).
@@ -964,3 +1134,71 @@ until the combined-panel semantics are verified. Malformed add-ons also refuse w
964
1134
  an order; requested add-on markers are never silently discarded.
965
1135
  Panel and custom orders share account/billing checks for home and walk-in collection.
966
1136
  The native profile keeps its existing panel-only contract.
1137
+
1138
+ ### Observed profile and oracle label
1139
+
1140
+ `profile: "observed"` with `oracle: "sandbox" | "production"` is the general form of
1141
+ `sandbox-observed` (an alias that always reports `oracle: "sandbox"`), so
1142
+ `GET /__admin/lab-account-behavior` says which vendor environment a configuration reproduces.
1143
+ Account-owned `rejectBilling` / `allowBilling` observations apply to either oracle, per
1144
+ account and never as a blanket delegated-account rule: a delegated customer account refuses
1145
+ explicit `client_bill` only when its own entry says so. The October 2026 sandbox omission
1146
+ recordings (#439) are sandbox observations, so they apply only to `oracle: "sandbox"`; with
1147
+ `oracle: "production"` an omitted account selects the house account as `expected-contract`
1148
+ does. Whether production also refuses a delegated account when `billing_type` is omitted is
1149
+ unverified, so it is not modelled. `oracle` on any other profile, or `production` on the
1150
+ `sandbox-observed` alias, is refused.
1151
+
1152
+ ### Omitted house-account billing (#467)
1153
+
1154
+ A reporter observed production accepting an AZ `client_bill` order with no `lab_account_id` on
1155
+ a team whose house BioReference account lists only `patient_bill_passthrough` for NY and NJ,
1156
+ placing it on the house account (its requisition prints the house account number). That is a
1157
+ single consumer report, not a repository probe, so the default is unchanged (the listing is
1158
+ enforced). Opt in per lab:
1159
+
1160
+ ```ts
1161
+ import { createRuntime } from "@crvouga/mockingbird-service-junction"
1162
+
1163
+ createRuntime({
1164
+ labAccountBehavior: {
1165
+ profile: "expected-contract",
1166
+ omittedAccountBilling: { bioreference: "unchecked" },
1167
+ },
1168
+ })
1169
+ ```
1170
+
1171
+ With `unchecked`, an order whose **root** `lab_account_id` is omitted (a nested-only id is
1172
+ ignored) and that resolves to a Junction-owned account (`org_id: null`) skips that account's
1173
+ listed billing and state check. The order keeps the house account's identity on POST, GET and
1174
+ the requisition. Explicit selection of the house account keeps its listed policy (or an
1175
+ account-owned `rejectBilling`), and a customer-owned account chosen by routing keeps its own
1176
+ listing. `GET/PUT /__admin/lab-account-behavior` read and change `omittedAccountBilling`.
1177
+
1178
+ ### Where an order's account id was sent (#473)
1179
+
1180
+ `GET /__admin/orders` adds `lab_account_request: { labAccountId, nestedLabAccountId }` per order
1181
+ (`instance().orderLabAccountRequest(id)`): the id carried at the request root, which selects
1182
+ the account, and the one nested in `order_set`, which Junction ignores. Each is `null` when
1183
+ absent. The request body itself is still not stored; orders seeded by a fixture have
1184
+ `lab_account_request: null`.
1185
+
1186
+ ### Sandbox quirks
1187
+
1188
+ Behaviors the sandbox shows that clients must tolerate, every one off by default
1189
+ (`createRuntime({ quirks })`, or per namespace `GET/PUT /__admin/quirks`; they survive `reset()`).
1190
+
1191
+ | Quirk | Effect |
1192
+ | --- | --- |
1193
+ | `sameSecondCancelStatus` (#474) | `POST /v3/order/{id}/cancel` in the same wall-clock second as the order's creation records a `cancelled.<method>.cancelled` event with the same timestamp, but `status`, `last_event` and `updated_at` stay `received`; a repeat cancel is a no-op. A cancel at least one second later reads back `cancelled`. |
1194
+
1195
+ This was reported from the sandbox (not re-measured here, production unverified). The quirk does
1196
+ not publish a webhook or cascade-cancel appointments for the stale cancel; the report does not say
1197
+ what the vendor does there.
1198
+
1199
+ ### Requisition text layout (#464)
1200
+
1201
+ The synthetic requisition PDF prints two text lines: `Requisition <order id>` and
1202
+ `Account number: <provider number>` (`unknown` when the order has no frozen number), so an
1203
+ extractor that accepts a field label only at the start of a line finds it. The layout is for
1204
+ interoperability, not vendor pixel fidelity.
package/SUPPORT.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Generated from `openapi.yaml`; do not edit by hand.
4
4
 
5
- - operations in spec: **40**
6
- - supported by the emulator: **40**
5
+ - operations in spec: **42**
6
+ - supported by the emulator: **42**
7
7
  - parity enabled: **22**
8
8
 
9
9
  | operationId | route | emulator | parity | notes |
@@ -17,13 +17,15 @@ Generated from `openapi.yaml`; do not edit by hand.
17
17
  | `patch_user_info_v2_user__user_id__info_patch` | `PATCH /v2/user/{user_id}/info` | ✅ supported | ✅ | |
18
18
  | `get_latest_user_info_user_v2_user__user_id__info_latest_get` | `GET /v2/user/{user_id}/info/latest` | ✅ supported | ✅ | |
19
19
  | `get_paginated_lab_tests_for_team_v3_lab_test_get` | `GET /v3/lab_test` | ✅ supported | ✅ | |
20
+ | `estimate_order_set_pricing_v3_lab_test_estimate_order_set_pricing_post` | `POST /v3/lab_test/estimate_order_set_pricing` | ✅ supported | ❌ disabled | prices are contract-owned; the emulator prices from the configured catalog (lab test price, marker price) and models the documented shape only |
20
21
  | `get_team_lab_accounts_v3_lab_test_lab_account_get` | `GET /v3/lab_test/lab_account` | ✅ supported | ❌ disabled | lab-account inventory is fixture-owned; the emulator models the documented shape, filters and routing rules, not a live account inventory |
21
22
  | `get_lab_tests_for_team_v3_lab_tests_get` | `GET /v3/lab_tests` | ✅ supported | ✅ | |
22
23
  | `get_lab_test_for_team_v3_lab_tests__lab_test_id__get` | `GET /v3/lab_tests/{lab_test_id}` | ✅ supported | ✅ | |
23
24
  | `get_labs_v3_lab_tests_labs_get` | `GET /v3/lab_tests/labs` | ✅ supported | ✅ | |
25
+ | `get_markers_v3_lab_tests_markers_get` | `GET /v3/lab_tests/markers` | ✅ supported | ❌ disabled | the marker inventory is the team's catalog (and any configured marker catalog); prices and availability are account-owned, so the emulator models the documented shape and filters, not a live inventory |
24
26
  | `get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get` | `GET /v3/lab_tests/{lab_test_id}/markers` | ✅ supported | ✅ | |
25
27
  | `list_order_set_markers_v3_lab_tests_list_order_set_markers_post` | `POST /v3/lab_tests/list_order_set_markers` | ✅ supported | ✅ | |
26
- | `create_order_v3_order_post` | `POST /v3/order` | ✅ supported | ✅ | Native profile parity only; explicit account profiles add fixture-owned billing policies and custom-marker orders, without production parity certification. |
28
+ | `create_order_v3_order_post` | `POST /v3/order` | ✅ supported | ✅ | Native profile parity only; explicit account profiles add fixture-owned billing policies and custom-marker orders as a best guess, not oracle-verified. Account-specific billing refusals are opt-in report-derived behavior, not a global vendor rule. |
27
29
  | `get_area_info_v3_order_area_info_get` | `GET /v3/order/area/info` | ✅ supported | ❌ disabled | central_labs presence, within_radius counts and getlabs market coverage are provider-owned geo data that an emulator cannot replicate per-zip; covered by the client-parity scenario shape checks instead |
28
30
  | `get_psc_info_v3_order_psc_info_get` | `GET /v3/order/psc/info` | ✅ supported | ❌ disabled | patient_service_centers are real site inventories per zip (thousands of sites, result-capped at 30) that a fixture emulator cannot replicate per-zip; covered by the client-parity scenario shape checks instead |
29
31
  | `get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post` | `POST /v3/order/phlebotomy/appointment/availability` | ✅ supported | ❌ disabled | availability slots are provider-side data whose values shift between sandbox calls; covered by the emulator-internal scheduling property suite instead |