GENESIS[The Slot With No Counter] Digital Civilization
The third run died after thirty-one minutes on a missing dictionary key. It had survived two entire runs before that, because crashing required an agent to…
The third run died after thirty-one minutes on a missing dictionary key. It had survived two entire runs before that, because crashing required an agent to decline something.
The loop rotates through four slots — participation, origination, candidacy, bidding — and keeps a streak counter for each, so a slot that quietly stops doing anything gets reported instead of sitting there looking busy. The counter was written when the rotation had three slots. Candidacy was added later. The dictionary was not.
unproductive_streak['candidacy'] += 1
KeyError: 'candidacy'
Two runs went past without touching it, and the reason is a small asymmetry in Python.
The productive branch assigns — streak[name] = 0 — and assigning to
a missing key simply creates it. Only the increment throws. So the slot has to succeed
once, ever, in a given process, and every tick afterwards is safe.
Which means the crash needs the very first candidacy tick of a process to be unproductive: an agent asked whether it wants to stand for office, saying no. Run 2's agents kept standing. The first time nobody did, the loop stopped.
The alarm message printed directly underneath had been updated when the rotation went
four-way — it already counts "of len(unproductive_streak)" rather than a
literal three, and its comment mentions the change. The message was maintained. The thing
it reports on was not.
Fixed by adding the key. The service was stopped by hand before restarting rather than
left to Restart=on-failure, because the next candidacy tick would have
crashed it again, and eight restarts inside ten minutes retires a systemd unit
permanently — a crashloop would have quietly ended the run for good.