Skip to content
Yoann Frayce
All systems
July 2026VSLive! Hack-a-thon · Microsoft HQ

Forkcast

Chapter 2 — making the boundary the product

A decision engine where every displayed figure is a typed claim that must round-trip to a simulation field, and a generated paragraph containing one unsupported number is discarded whole.

Sole authorPrivate repositoryView architecture
Trials per plan
500
paired, common random numbers
Tests
190
zero build warnings, warnings-as-errors
Domains on one engine
2
electric depot and GPU compute hall
Verified claims in the shipped decision
8
0 unsupported numbers

Figures with a dotted underline carry their source. Hover one to see whether it was verified in code or reported in project documentation.

Context

The situation

DepotOps proved that simulated futures make an operational decision legible. It also exposed the real risk: the moment a language model narrates those numbers, it can produce one that never came from the engine.

The problem

A model will confidently write “87% on-time” and “£4,200 saved”, and those figures come from nowhere. A wrong number that reads fluently is worse than no number at all, because someone acts on it. Prompt instructions do not fix this — nothing checks them.

What I built

Make provenance a type. Every displayed figure is a Claim carrying the simulation field it came from, how it was calculated, the seed and the trial count, and it is Verified only if it still round-trips to that field. Generated prose is then scanned number by number against the claim set. One unsupported figure discards the whole paragraph and the deterministic summary is shown instead.

When an operational site breaks, the hard part is which response to take in the next ten minutes. Modelling both options properly takes longer than the decision window, and asking a language model produces fluent figures that come from nowhere. Forkcast splits the problem along the line where each tool is good. A deterministic engine runs 500 paired Monte Carlo trials per response plan, and every figure on screen is a typed claim carrying its source field, calculation method, seed and trial count. The model writes the explanation; a verifier then scans every number in that prose and discards the paragraph if one of them is unsupported.

Architecture

How it fits together

Generated explanation

Reprioritising lifts on-time departures to 97.2%, leaving 1 of 20 at risk and avoiding £4,200 of penalties.

97.2%supportedalternative.onTimeDeparturePct
1 of 20supportedalternative.vehiclesAtRisk
£4,200unsupportednothing produces this figure

→ paragraph rejected · the deterministic summary is shown instead

  1. Incident textinput

    in the operator’s words

  2. Structured extractionlanguage model

    model reads into a schema

  3. IncidentComposerguard

    clamps to a site that can exist

  4. Simulationdeterministic

    500 paired trials per plan

  5. Claim setdeterministic

    each figure round-trips to its field

  6. Generated proselanguage model

    model writes the explanation

  7. Claim verifierguard

    scans every number

  8. Recommendationoutput

    or the deterministic summary

Forkcast.Core has no dependency on ASP.NET, on HTTP, or on any model. It is the part that computes, and it runs and tests entirely on its own.

Key decisions

The choices that shaped it

Each of these was a fork in the road where the obvious option would have produced something that looked the same and behaved worse.
  1. 01

    Claims, not values

    A figure is not a number on a screen — it is a typed object carrying the field it came from, the calculation that produced it, the seed and the trial count. It is Verified only if it still resolves to the simulation output it names. Edit the value and verification fails.

    ClaimSetBuilder.Resolve

  2. 02

    Rejection, not correction

    When the verifier finds one unsupported figure, it discards the entire paragraph rather than patching that figure. A narrative that got one number wrong is not evidence about the others, and silently repairing it would hide the failure that matters.

    ClaimVerifier

  3. 03

    Identifiers and clock times are not quantities

    The verifier masks vehicle identifiers and clock times before checking, so “18:40” and a unit number are never treated as figures needing support. Getting this wrong would either flood the output with false rejections or force the allow-list wide open.

    Clock_times_and_vehicle_identifiers_are_not_treated_as_quantities

  4. 04

    Common random numbers

    Both response plans are evaluated against the same sampled nights. The gap between them reflects the plans, not sampling noise, so one plan cannot look better by accident.

  5. 05

    SplitMix64, implemented in-tree

    The generator is written in the repository rather than taken from System.Random, so anyone can regenerate the published figures from the published seed on any platform. A test pins a derived seed literally, to catch that ever changing.

    Derived_seeds_do_not_depend_on_runtime_string_hashing

  6. 06

    Two domains, one engine — asserted by test

    An electric depot and a GPU compute hall share no vocabulary, units or failure mode, and run on identical simulation, comparison, claim and recommendation code. The vocabulary is data. Tests assert that neither domain’s critical-constraint string contains the other’s nouns, so “it generalises” is a property rather than an adjective.

    The_critical_constraint_speaks_the_domain_language

  7. 07

    Two languages, and provably one arithmetic

    English and French are hand-written text packs rather than runtime translation, precisely so no model is ever in a position to translate a number. The verifier is taught to read the forms the French ledger prints — a checker that could not parse its own output would report the product’s own figures as invented.

    A_french_run_and_an_english_run_are_the_same_simulation

Evidence

What it actually looked like

The Forkcast verification panel, listing every claim with its source field and calculation.

Every number, accounted for. Expand any claim for its source field and calculation.

Interface
Two response plans drawn side by side on one shared scale.

Two futures, on one shared scale, so the plans can be read against each other.

Interface
The verifier probe, giving a verdict on every number in a submitted paragraph.

Try to fool it: every number in a submitted paragraph gets a verdict.

Interface

Measured

Every figure, with its source

Trials per plan
500
paired, common random numbers
Tests
190
zero build warnings, warnings-as-errors
Domains on one engine
2
electric depot and GPU compute hall
Verified claims in the shipped decision
8
0 unsupported numbers
Published seed
20260728
reproducible on any platform

Figures with a dotted underline carry their source. Hover one to see whether it was verified in code or reported in project documentation.

Recognition

Result

Best Solo HackerVSLive! Hack-a-thonMicrosoft HQ, Redmond · 29 July 2026

~100 participants

Awarded for the most impressive individual project.

Holding the VSLive! Hack-a-thon award cheque at Microsoft HQ. It reads Best Solo Hacker, made out to Yoann Frayce, dated 29 July 2026, with the memo line “most impressive individual project”. A sponsor banner behind names Microsoft as event partner.

Best Solo Hacker — most impressive individual project. Microsoft HQ, 29 July 2026.

Award

The result is context. What the system does, and how it was measured, is everything above this.

My contribution

Sole author — engine, verifier, API, interface, tests and the submission film.

What I owned

  • Deterministic simulation engine with an in-tree SplitMix64 generator
  • The claim layer: typed claims, resolution, and round-trip verification
  • The claim verifier, its allow-list, and the public probe endpoint
  • A second domain proving the engine is not shaped like the first
  • Bilingual output where the arithmetic is provably identical across languages
  • Minimal API, React interface, 190 tests, and the evidence-synced decision film

Stack and limits

Built with

  • C#
  • .NET 9
  • ASP.NET Core Minimal API
  • xUnit
  • React
  • TypeScript
  • Vite
  • Azure OpenAI

Techniques

  • Typed claim provenance
  • Post-generation claim verification
  • Common random numbers
  • Deterministic PRNG (SplitMix64)
  • Constrained extraction into a clamped schema
  • Domain vocabulary as data

What it is not

  • The simulation is decision-support, not physics: no charge curves, no thermal model, no degradation.
  • Operational data is synthetic, and the interface says so on the situation card.
  • It compares two named strategies head to head. It does not search for the best one.
  • The verifier checks numerals, not claims about causation. A paragraph can pass and still be misleading in its wording.

What carried forward

The second domain was the point. Once a GPU compute hall ran on the depot’s engine without a line of change in the simulation, the comparison or the claim layer, it was clear the valuable asset was never the depot model — it was the verified decision loop around it. That is what FleetMind builds on.