I built a proof of concept for the eligibility-to-cohort authoring step: an LLM reads the verbatim registered criteria of a trial at build time and emits a standard Circe cohort definition, committed only after passing four automated gates. The runtime is unmodified OHDSI tooling (CirceR, SqlRender, DatabaseConnector), no model in the execution path. Where the prose admits no single determinate encoding over the CDM, the criterion compiles to AMBIGUOUS with a rationale, never to a guess.
NCT03667300 (evogliptin, T2DM with renal insufficiency) is the example: 16 criteria, of which 10 compile determinate and 6 refuse. The gates: valid Circe structure with every concept_id resolving against a pinned vocabulary subset; a bijection between source sentence ids and manifest entries, so no criterion is silently dropped or invented; execution against 29 synthetic patients with expected labels, including pinned boundary cases (eGFR 30 vs 29.9, HbA1c 7.0 vs 6.9); and byte-identical recompile.
The behavior I think is worth a look is the refusal on E6, “strong CYP3A4 inhibitors or inducers”. A patient on itraconazole would be correctly excluded by a guess, itraconazole is a strong CYP3A4 inhibitor, and the tool refuses anyway, because there is no vocabulary-native concept set for the DDI-strength category, so encoding it means inventing membership. The refusal ledger in docs/AMBIGUOUS.md lists all six with what would make each determinate.
Honest state: one trial, synthetic fixtures with author-asserted labels, not clinically adjudicated, one Athena snapshot pinned. It is a methodology PoC, not a recruitment tool and not a benchmark. Chia and n2c2 are treated as evaluation-only and never enter the compile input. Closest prior work is Criteria2Query; the difference attempted here is the committed, fixture-verified artifact and the tested refusal class rather than best-effort extraction.
Gates 1, 2 and the determinism check run with Python only, no API key: clone, uv venv && uv pip install -e '.[dev]', python -m pytest -q. Full execution path runs in the devcontainer.
The question for people who author cohort definitions by hand: do the six refusals land where you would expect? If an experienced phenotyper looks at the AMBIGUOUS list and says “E5 is actually encodable, here’s how” or “you missed that I6 is also indeterminate”, that is the most useful thing I could learn from this community.