Every hireable file in the league — coaches and players — belongs to at most one
team at a time. The system has three parts: a status flag on the hireable file, a
filtered reference on the team file, and a two-write bookkeeping rule that keeps
the two in sync. This chapter explains how the pieces fit and how to extend them
without breaking the guarantee.
The two availability flags
| File type | Field | Free agent | Under contract |
|---|
| Staff (coaches) | contractStatus | free-agent | signed + signedWith: "<Team Name>" |
| Player (athletes) | rosterStatus | unsigned | active (or practice-squad, injured-reserve, suspended, retired) |
The staff flag exists purely for the market; signedWith is bookkeeping text that
names the holding team so audits are one grep away. The player flag does double
duty — it was already the in-season status (the team's availableCount computation
reads it), and unsigned doubles as "nobody holds my rights." New player files
default to unsigned, so a freshly created athlete is immediately signable; new
staff files omit the field, and the filter treats absent as available.
The filters on the team file
Every hiring slot on the Team type is a filtered reference:
- The four staff chairs (
headCoach, offensiveCoordinator, defensiveCoordinator,
specialTeamsCoordinator) offer only staff whose contractStatus ≠ "signed".
- Roster rows offer only players whose
rosterStatus = "unsigned".
The coach filter uses ≠ rather than = "free-agent" on purpose: a staff file
missing the flag counts as available, so bulk-created coach pools never need a
seeding pass to appear on the market. The player filter uses = because every
non-unsigned rosterStatus — including injured-reserve and retired — is not
signable: an IR player is under someone's contract, and a retired player is gone
for good.
A stored reference that fails its filter still saves with a soft warning. This is
deliberate: it keeps existing assignments intact when a flag was missed, and the
warning itself is the audit trail. Treat any warning as a bookkeeping error to
fix, never as a prompt to delete the reference.
The two-write rule
References are one-directional — a team file knows its coach, but the coach file
cannot see who points at it. So a contract exists only if both sides are written,
and the platform cannot do this automatically. Every transaction is two writes:
Sign — set the team's reference, then flip the flag:
update /Teams/<Team>.team.json → staff.offensiveCoordinator: "<coach-slug>"
update /Staff/<Coach>.staff.json → contractStatus: "signed", signedWith: "<Team Name>"
Release or cut — remove the reference, flip the flag back:
update /Teams/<Team>.team.json → delete staff.offensiveCoordinator
update /Staff/<Coach>.staff.json → contractStatus: "free-agent", clear signedWith
No trades (for now) — trading is disabled in this ruleset because background
rosters carry only a handful of signed players, not full 53s. There is no trade
transaction to write; if trading ever returns, it would swap the references on both
teams and update signedWith while rosterStatus stays unchanged.
Re-signing an expiring contract — release first, then sign, in the same
sitting. The release briefly exposes the player to poaching, which is the correct
football answer: during that window anyone can sign him.
The live GM carries this discipline during play (the League Office instructions
spell out the verification and the two-write sequence); the flags also update
automatically whenever a signing is requested of the project assistant.
Auditing the league
Because the flags are plain fields, the whole league's contract state is greppable:
grep "contractStatus" /Staff/ → every coach and who holds them
grep "\"rosterStatus\"" /Players/ → every athlete's market state
grep "<coach-slug>" /Teams/ → which team files reference a coach
Run the third command whenever a signing feels off — a coach referenced by two team
files with only one flag flipped is the classic missed-write failure, and it shows
up there.
Extending the system
- New hireable type (e.g., front-office staff): add the same
contractStatus
enum, then attach the same ≠ "signed" filter to whatever reference field hires
them. The pattern is the constraint, not the field names.
- Multi-year coach deals: athletes already compute
contractExpired from
contractLastYear vs. the season log. If coaches ever get term deals, mirror
that pattern — keep contractStatus as the simple gate and let a computed
expiry field drive offseason retention decisions.
- Designated players or franchise tags: express them as a third enum value on
the status field and extend the filter with an
or group — filters support up
to three comparisons.
- Do not encode availability on the team side (e.g., a "who do I hold" list).
The flag lives on the hireable file precisely so every team's picker reads one
source of truth.