DEV Community

Cover image for The call sheet is not a table
Srikanth Reddy Kasa for Supero

Posted on Originally published at docs.supero.dev

The call sheet is not a table

Backlot is an open-source film production scheduling app. It has five tables and none of them is the call sheet.

On a film set, the call sheet is the document that tells you where to be and when. Every production has one. It is the single most-copied, most-emailed, most out-of-date artefact in the business.

So when we built Backlot, an open-source film production scheduler, the obvious move was a call_sheet table.

We didn't build one. Here is what we built instead, and why it turned out to be less code.

Five tables, no call sheet

ALL_SCHEMAS = [Production, Person, Scene, ShootLocation, Assignment]
PUBLIC_SCHEMAS = ["production"]
Enter fullscreen mode Exit fullscreen mode

That is the entire data model. The slate of productions, the roster of people, the scenes, the locations, and the assignments that connect a person to a production.

An Assignment names a production and a person, gives a role with a call time and a location, states a day rate, and ends with an owner_username.

That last field is the whole trick.

Written by one person, owned by another

When the production office offers someone a role, the save handler does this:

if (d.person_email) d.owner_username = d.person_email;
Enter fullscreen mode Exit fullscreen mode

The row is created by the line producer. It belongs to the cinematographer.

Then the crew-facing side is almost nothing:

client.getObjects('assignment')
Enter fullscreen mode Exit fullscreen mode

No filter. No where owner_username = me. No tenant argument. The component sorts
by call time, groups by state, and renders.

The filter lives on the server:

PolicyDef(role="tenant_user", default_access="none", rules=[
    PolicyRule(entity="production",      can_read=True),
    PolicyRule(entity="person",          can_read=True),
    PolicyRule(entity="scene",           can_read=True),
    PolicyRule(entity="shoot_location",  can_read=True),
    PolicyRule(entity="assignment", can_read=True, can_create=True, can_update=True,
               filter_field="owner_username", filter_match="$user.name"),
])
Enter fullscreen mode Exit fullscreen mode

The seed writes twelve assignments. Five carry the demo crew account's email. So that login has a five-row call sheet — two confirmed, two offered, one declined — and there is no code anywhere that says "five".

The call sheet is not a table. It is a row filter with a name.

You can check the boundary without an account

Backlot publishes its slate and nothing else. Two unauthenticated requests:

curl -s https://backlot.supero.live/api/public/production
curl -s -o /dev/null -w "%{http_code}\n" https://backlot.supero.live/api/public/assignment
Enter fullscreen mode Exit fullscreen mode

On 27 October 2026 the first returned six productions. The second returned 403, with a body saying the schema is not public. The same request against person also
returned 403.

The slate is the shop window. Who is called where, at what hour, for what rate, sits behind a login — and even then, each person sees only their own rows.

The handshake runs both ways

ASSIGNMENT_STATES = ["offered", "confirmed", "declined", "wrapped"]
Enter fullscreen mode Exit fullscreen mode

Of the nineteen apps in our repo, Backlot's is the only schema file with an offered state. That is not a stylistic detail. It means a row can exist that the recipient has not agreed to yet.

The office creates the assignment at offered. An event binding on @create:backlot:assignment runs a workflow that emails the person. They press Confirm or Decline on their own row — and that update is the only write the crew
portal makes.

The office can also confirm from its side, with a workflow declared "on_error": "compensate". Three steps: set the state to confirmed, email the call time and location, send the same by SMS. The first step carries a compensation that writes back the prior state, so a failed email leaves the assignment where it was
rather than showing a confirmation nobody received.

The SMS step is "on_error": "continue" and cannot undo the other two. That is a deliberate choice: a missing text message should not roll back a confirmed call.

What this buys you

The crew portal is a list and two buttons. There is no "my assignments" endpoint, no user-scoped query, no if (user.role === ...) anywhere in the client. Delete the whole front end and the access rules are unchanged, because they were never in it.

The thing a call sheet actually is — the subset of today's schedule that concerns you — turns out to be a property of who is asking, not a document that has to be generated, stored and kept in sync.

Backlot is open source. Code: github.com/supero-platform/supero-apps, under apps/media/backlot. Live: backlot.supero.live.

Disclosure: I work on Supero, the platform this is built on.

Top comments (0)