♟️ From Jev to Darkwaar: Building a Game About Decisions and Uncertainty
on September 27, 2026
Jev is TypeSafe's first System One model. You send a state and a set of typed questions. It sends back typed answers, with probabilities where the question has more than one slot. There is no paragraph to parse.
A generative language model does the other job. It continues text. You then decide whether that text was a department name, a score, or a refusal. Jev's contract is the decision itself: the allowed answers are the ones you listed, and the response is one of those answers plus a distribution.
This week I did not wire Jev into Darkwood Flow. I used the study of that API to build a small game about the moment before a commit: do I know enough?
Three question types
The HTTP call is POST https://api.typesafe.ai/v1/systemone. The body is a model name (jev-latest is the alias I used in the study), a state, and a map of questions. Official docs, retrieved for the study on 21 September 2026, describe three question types.
Choice. Which of these options? The answer carries choice, probabilities, and confidence. The probabilities are a distribution over the criteria you supplied.
Score. Where on this ordered rubric? The answer carries score, a legend that repeats the rubric, probabilities, and confidence. The score is a position on the scale, so it can sit between two labels.
Noul. Is this statement true? The answer carries noul, a float from 0 to 1. That field is the model's degree of support for the statement. It is not a boolean, and the documented noul answer does not carry a separate confidence field. Choice and score do.
One request evaluates every question against the same state. The docs describe those questions as independent: a later question that needs an earlier answer is a second request. I did not measure that latency here. The study's unauthenticated probe of the live API, with no key, returned HTTP 403 and authentication_error. TYPESAFE_API_KEY was empty, so this article does not report live confidence numbers, agreement rates, or timings.
Probabilities are not a guarantee
Confidence, on choice and score, is a number you can branch on. The useful shape in application code is ordinary:
if confidence is high and the choice is billing → route
if confidence is low, or noul sits near 0.5 → escalate
That branch is a policy. It is not a proof. A high confidence can still be the wrong department, because the number describes how peaked the distribution is, not whether the world matches it. A noul of 0.99 is strong support for the statement. It is still a float you chose to treat as yes.
The study kept those fields intact on the way through Symfony AI's ObjectResult. It did not collapse them to a boolean inside the model client. Low confidence is a successful response. It is not an exception.
The same study has a local Platform bridge under /Users/math/Sites/tests/ai, package shape symfony/ai-jev-platform, capability INPUT_TEXT only. That bridge is local research. It was not submitted, merged, or released as part of this week's work. Symfony AI still has no core capability named decision, choice, or noul. OUTPUT_STRUCTURED means Symfony's JSON-schema path for a generative model. Jev's structure is the questions in the request. Those are different mechanisms, and the bridge does not pretend otherwise.
From a distribution to a puzzle rule
Darkwaar15 — Signal does not call Jev. There is no API key, no network request, and no sampled answer. The three names are the three operations the player commits:
- NOUL asks whether the lit marks are even.
- CHOICE asks which listed pattern matches the marks. A lit mark is
1, a dark mark is0. - SCORE asks which band holds the sum of the bars: low is 0–1, mid is 2–3, high is 4 and above.
Some wells start closed. A square counts for the current operation. A diamond is noise and never changes the answer. Opening a well reveals a value that was fixed when the level was written.
Confidence in the game is a collapse of the remaining answers, not a calibrated probability:
confidence = 1 when one answer is still possible
confidence = 1 - (possible - 1) / (universe - 1) otherwise
The universe is 2 for a noul, the number of patterns for a choice, and 3 for a score. The gate on every level is 0.70. One remaining answer is confidence 1, which clears the gate. Two remaining answers out of three is 0.50, which does not. The player can press commit anyway. Below the gate the console answers "Not enough signal" and does not reveal the reading. On or above the gate, a wrong output shows the reading and counts a miss. Three misses reset the level.
That is the translation. Jev's confidence can refuse to act. The puzzle makes the refusal the rule, and makes the hidden wells the reason the answer is not unique yet. Level 7 is the clearest case: two bars are enough for the sum to sit in high even if a third bar is still closed, because that bar's ghost height cannot leave the band. Opening the diamond does nothing. The interesting commit is the one you make before every well is open.
The solutions are data, checked by a headless Godot script that replays the scripted probes for all eight levels and then drives the real scene through a wrong commit, a low-confidence reject, a miss that shows the reading, a restart, and a clear. Both checks printed pass before the HTML5 export.
The console
The game lives in the Darkwaar monorepo as 2026-09-27-01M3H4X9806QZ6Z7D85F, legacy id darkwaar15. Godot 4.6, portrait viewport 648×1152, one Control scene. Three scripts do the work:
rules.gdholds the eight levels and the confidence rule. It does not draw.signal_view.gddraws the wells, the connectors, and the confidence meter. The tick on the meter is the gate.main.gdis the commit loop: probe, select, commit, miss, restart, and the last screen.
Input and evaluation and output are stacked on the portrait canvas. Closed wells keep their shape, so a diamond is visible before you waste a probe on it. The meter is cyan under the gate and amber once the answer is unique. Mouse and touch both hit the wells and the buttons.



The web export is the same scene. bin/build-game darkwaar15 --validate --zip packed main.gdc, rules.gdc, and signal_view.gdc. bin/smoke-web-build darkwaar15 loaded the canvas at 648×1152, found a non-blank frame, and reported no fatal page errors. A click on commit in the browser build answered "Choose an output" when nothing was selected, so the HTML5 loop is taking input.
Publication
The game is on itch.io at darkwoodcom.itch.io/darkwaar15. The HTML5 channel is the build this article describes. I requested that URL and got HTTP 200.
The Darkwaar post is darkwaar.com/blog/2026-09-27-darkwaar15. It answered HTTP 200, and the page includes the how-to-play note, the three screenshots, and the itch link.
Flow stays a pipeline
The original study asked whether Jev belonged inside Flow. The answer from the code, unchanged this week, is that Flow already has the place a decision sits. A job is JobInterface: one input, one return value. The packet is an Ip. Replacing a rule job with a Jev job changes what the packet carries. It does not change the driver, the IP strategy, or the stage graph.
I did not add Decision<T>, Choice<T>, or a JevDriver to darkwood/flow. I did not run a Flow integration test, and I did not publish one. A low-confidence answer would still be a normal job result. Escalation would still be the next job's PHP. That work is postponed. This week's artifact is the research, the local Platform bridge left unsubmitted, and Darkwaar15.
What the experiment is for
A decision procedure is a state, a closed set of answers, and a rule for when you are allowed to act. Jev is a remote version of the first two, with a distribution attached. Darkwaar15 keeps the closed set and replaces the distribution with a fact the player can see: how many answers the closed wells can still reach. The gate is the third part, and it is the part the application owns.
The console will refuse a correct button pressed too early. That is the property I wanted to feel. Enough information is a state of the puzzle, in the same way enough confidence is a state of the program, and neither number is a promise that the commit was right.