--- # Authoritative formal design for realtime machine M1: connection lifecycle. # # Companion to: # - docs/design/realtime-state-machines.md (the map + invariants) # - core/http/endpoints/openai/conncoord (the Go implementation) # # The Go MBT adapter maps each action below onto conncoord.Coordinator.Apply and # the StateGetter onto conncoord.Coordinator.State, so this spec is the source of # truth the implementation is checked against. # # The legacy hazard (Part 2, failure mode 6 / invariants #8, #10): a single `done` # channel reassigned on every VAD toggle and closed from two sites (toggle-off and # teardown) guarded only by a vadServerStarted bool. Modeled here as `running` # (the VAD goroutine's done channel is live) and `torn` (teardown happened). # # NOTE: FizzBee is pre-1.0. Validate the exact syntax/CLI against the version # pinned in formal-verification/README.md before trusting the gate. deadlock_detection: false --- role Conn: action Init: self.running = 0 # VAD goroutine running (its done channel is live) self.torn = 0 # teardown has happened self.teardowns = 0 # how many times teardown ran -- MUST stay <= 1 # session.update toggled turn detection on. No-op after teardown (the legacy # reassign-and-spawn must never resurrect a torn session). atomic action VadOn: if self.torn == 0: self.running = 1 # session.update toggled turn detection off (close the running done channel). atomic action VadOff: if self.torn == 0: self.running = 0 # Transport read loop ended / session closing. THE FIX: setting torn absorbs # every later Close, so teardown's channel closes happen exactly once. To # reproduce the legacy double-teardown hazard, delete `self.torn = 1` below: # the checker then reports TeardownOnce violated (Close runs teardown again). atomic action Close: if self.torn == 0: self.running = 0 # StopVAD if it was running (close-once) self.teardowns += 1 # Teardown self.torn = 1 action Init: c = Conn() # SAFETY: teardown runs at most once -- the done/decode/sound channels are closed # exactly once, never double-closed (Part 4, invariant #10). always assertion TeardownOnce: return c.teardowns <= 1 # SAFETY: the VAD goroutine is never (re)started after teardown -- no # send-after-close / no goroutine outliving the session (Part 4, invariant #8). always assertion NoRunAfterTorn: return not (c.torn == 1 and c.running == 1)