-
-
Constructed overnight-location demo: four paid hold days across three performers become zero after solving under modeled rules.
-
The agent panel shows its tool calls and registry refusals. Source lookups reject unknown rule IDs; model prose needs review.
-
A flagged day shows modeled rule violations, with citations, scheduled values and limits.
-
Open a rule to read its source quotation. CI checks quoted text using documented extraction normalization.
-
Drag strips to rearrange the board locally. Solve computes a new day assignment under the modeled constraints.
-
The mobile Day view shows the recorded before schedule. It does not synchronize with a desktop solve.
The problem
A shooting schedule is a math problem, and almost nobody gets to solve it as one.
On an overnight location, paid hold days can accrue between a performer's first and last scenes. A minor's schedule also has sourced limits on time at the workplace, working hours, rest and meals. An assistant director has to make those constraints fit the same production board.
We wanted the version of that whiteboard where the math is already done, and the modeled rules are checked before the day is shot.
What HOLD does
You give it the scene list, who is in each scene, the shooting days and the rate tier. It gives back two things.
Minimum hold-day payroll under modeled constraints. A CP-SAT model assigns scenes to days and orders them, with the implemented scheduling limits as hard constraints. It reports whether optimality was proven and the bound reached when it was not. In the disclosed constructed overnight-location example, the before plan has 4 paid hold days across 3 performers and 1 day that fails a modeled rule. The solved plan has 0 of each. The difference is $4,069.92 of modeled payroll at the published SAG-AFTRA low budget day rate plus pension and health. An independent Python recount agrees to the cent. This is not measured customer savings or a claim about total production cost.
A verdict for each checked day. An independent checker applies the implemented Georgia and California child-performer rules and the modeled SAG-AFTRA provisions. For a flagged limit, it shows the rule ID, citation, scheduled value, limit and source quotation. The registry contains 70 sourced records; that count is not a claim that every record is an optimizer constraint or that all labor law is covered. CI matches quotations against committed source text using documented extraction normalization. In the video's California case, the 9-hour limit concerns time at the workplace, not working hours.
The thing I did not expect to be able to say
You can run a published benchmark instance from an MCP client without cloning the repository.
HOLD runs its own MCP server on the deployed origin. Point an HTTP-capable MCP client at https://hold-fwmdq7fc3q-uc.a.run.app/mcp/ and you get four tools: solve_schedule, check_legality, lookup_rule and run_residual. Call run_residual on film103, a published academic talent scheduling instance with a proven optimal cost, and inspect these selected fields from its instance result:
{"name": "film103", "status": "OPTIMAL", "holding": 187, "published_holding": 187, "matched": true}
That is our solver, running on our production instance, finding the published optimum while you watch. Not a claim in a README. A tool call you make.
Everything else you can check in two minutes
- The app. Open the Board, press Solve and inspect the result. No sign-in is required.
- The service. /api/status reports configuration and committed measured results. Headline figures come from docs/FACTS.json.
- On a phone. Install the Android APK or join TestFlight. The Capacitor shells use the web codebase and live API. The Day view shows the recorded before schedule; it does not synchronize with a desktop solve or scene-drop event.
- Ask the agent. The Board panel answers questions about the checked rules and shows tool calls, arguments and refusals.
- Change the plan. Type "Drop scene 6.", review the interpreted proposal, then press Publish this event. Inspect the receipt and revised order.
The part we are proudest of
The residual. We took the academic talent scheduling benchmark, the one with published and proven optimal costs, and ran our solver against all eight medium instances. It matches every one. That test runs on every push with no key and no account, and the README's CI badge links to the workflow that includes that benchmark job. This checks agreement on those eight instances; it does not prove every possible schedule correct.
Where Google Cloud and IBM come in
Gemini 3.1 Flash Lite on Vertex AI, driven through the Google Agent Development Kit, is the agent, and there are three of them. One reads a call sheet, a one line schedule or a plain note and turns it into the typed schedule, and its instructions require it to ask about missing details instead of guessing. One answers questions about the board with three tools behind an allowlist, argument validation and a per request budget.
The third is the one I would show you first. Type what happened on set the way you would say it out loud, "B is out on Thursday", and it comes back as a typed event with its reading in plain English: "Performer B cannot work on Thursday, day 4." Nothing moves until you press publish. Then the same deterministic engine the buttons use decides what the change costs, because a number a model produced is a number nobody can check. The model reads the sentence. The solver does the arithmetic. That split is the whole design.
There are two eval sets, covering the tool-bearing agent and event interpreter, recorded from real runs into the repository. CI fails when a case fails, when a case is unscored, and when the record stops describing the agent that ships, which is a hash of the instruction, the tools, the model and the eval set. That last check caught its first real prompt change within an hour of going in and refused the stale score until the eval was re-run.
One thing that surprised us and is worth watching for on the board: asked a hard question, the agent reached for a rule id that does not exist, and the registry refused the call rather than answering with it. The panel shows that refusal in red next to the calls that worked. The tool rejects unknown rule IDs; the model's prose still needs review.
The API runs on Cloud Run and deploys from CI through Workload Identity Federation, with production credentials in Secret Manager. In the demonstrated path, a confirmed HTTP event starts the local solve and is mirrored to Confluent Cloud; results stream back through server-sent events and are published to the verdict topic. Confluent is not the trigger for the video's local solve.
IBM Bob built the entire project, including the custom modes, tests, and the later MCP server, even after exhausting three different accounts and their Bobcoins. The evidence directory holds the three exhaustion screenshots, the captured session export, per-commit attribution, and the build trace. Five write-scoped custom modes define the development lanes, and tests enforce their path rules. The honesty log notes anything that could not be captured. The export is install-wide rather than a precise HOLD-only total.
What we will tell you before you find it
The demo schedule is constructed and says so on screen. Our search did not find a reusable public corpus of production stripboards. Under the low budget agreements a hold day is paid only on an overnight location. The demo declares one, and the README explains the reading. The rest period, forced call and meal penalty rules exist as cent exact functions with their sources. The optimizer minimizes modeled hold-day payroll and does not price those penalties into the objective. The status page reads the headline figures from committed FACTS.json, generated from a real run and checked in CI. We wrote to five people across the union, the guild and the Georgia production community. One answered. A business representative at a SAG-AFTRA local confirmed the cumulative reading this engine implements, that California's rules follow a California resident minor to another state, and that a stricter Georgia provision prevails over them. That reply is worded more broadly than 8 CCR 11756, which conditions the out of state case on a California employer and a contract made in California, while we apply the California records on residency alone, and the README says exactly where the two readings differ. It is not an endorsement and it is not a legal opinion. It is recorded without a name because no attribution consent was given, and nobody has used HOLD on a real production.
What we learned
Rules as code are only as good as the sentence behind each number, and the sentence is where fabrication hides. Our first pass had quotes that read right and were not on the page. Now the check is a machine, and one union site that refuses scripts had to be captured from a real browser with the capture recorded in the header.
In our recorded tests, combining tool calls with a forced final output schema made this model repeat calls until it hit the call ceiling. We traced it live and split the agent in two. Then the same agent, given a complete call sheet, started checking legality on its own instead of handing the schedule back, which is exactly what the design forbids. The eval caught it, the recorded score dropped to 2 of 4, and it took two more instruction changes, read from the model's own answers, to bring it back to 4 of 4.
The last thing we learned is the one that changed the most code. We audited what the deployed app can actually reach, rather than what the repository contains, and the two were not the same. The import screen read your document, showed you four counts and then dropped the result on the floor, so a sentence we had written about the product was not true of the running system. The agent that carries the tools had no route at all, which meant three tools and the allowlist guarding them had never once executed in production. Both are fixed, both have tests, and we would not have found either by reading the code.
We did that audit again three days later, on a screen we had looked at a hundred times, and found that two of the three buttons under the board had never worked on any deployment. They sent the wrong key. Every test in the repository built its own payload, so nothing ever compared what the button sends to what the engine reads, and the end to end test clicked only the third button, the one that happened to be right. The typed sentence we had just shipped produced the correct keys from day one, which is how we noticed: the new thing worked and the old thing next to it did not.
Watch and try it
Watch the 2:55 demo, then open HOLD and run the same example. The film shows real product recordings, with two HeyGen presenter scenes using my approved avatar and cloned voice. iOS simulator and Android emulator footage is labeled.
Log in or sign up for Devpost to join the conversation.