Index: protocol/src/main/java/geniusweb/protocol/NegoProtocol.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/NegoProtocol.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/NegoProtocol.java	(revision 21)
@@ -8,4 +8,5 @@
 
 import geniusweb.connection.ConnectionFactory;
+import geniusweb.deadline.Deadline;
 import geniusweb.events.CurrentState;
 import geniusweb.events.ProtocolEvent;
@@ -26,5 +27,5 @@
  * {@link #getDescription()}. A protocol reports the progress through its
  * {@link Listenable} interface. <br>
- * <p>
+ * <h2>General information</h2>
  * 
  * A protocol is mutable because the incoming connections cause state changes.
@@ -33,9 +34,4 @@
  * Because a protocol contains an internal state, it can be used only once.
  * <p>
- * The protocol also needs to keep an eye on the deadline and take appropriate
- * actions when the deadline is reached. <br>
- * <p>
- * A protocol should emit a {@link SessionEnded} or {@link TournamentEnded}
- * event when it is finished.
  * <p>
  * The protocol can emit a {@link CurrentState} event at any time. It should do
@@ -48,8 +44,21 @@
  * {@link #start(SessionSettings, ConnectionFactory)}.
  * 
+ * <h2>Ensure time deadline</h2>
+ * 
+ * The protocol also needs to keep an eye on the deadline and take appropriate
+ * actions when the deadline is reached. <br>
+ * A protocol should emit a {@link SessionEnded} or {@link TournamentEnded}
+ * event when it is finished.
+ * <p>
+ * All protocol implementations must ensure that the deadline is kept and that
+ * the session ends at the agreed time {@link Deadline#getDuration()}. This is
+ * to ensure that the negotiation ends and resources are freed up at or before
+ * some known time.
+ * 
  */
 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
-@JsonSubTypes({ @Type(value = SessionProtocol.class), @Type(value = TournamentProtocol.class) })
+@JsonSubTypes({ @Type(value = SessionProtocol.class),
+		@Type(value = TournamentProtocol.class) })
 
 public interface NegoProtocol extends Listenable<ProtocolEvent> {
@@ -64,13 +73,13 @@
 	 * <p>
 	 * 
-	 * All errors are to be handled through {@link SessionState#getError()} except
-	 * for plain {@link IllegalArgumentException}s. <br>
-	 * The protocol usually uses the incoming connections to keep running. It does
-	 * not need to run in a separate thread or so.
+	 * All errors are to be handled through {@link SessionState#getError()}
+	 * except for plain {@link IllegalArgumentException}s. <br>
+	 * The protocol usually uses the incoming connections to keep running. It
+	 * does not need to run in a separate thread or so.
 	 * 
 	 * 
-	 * @param connectionfactory the {@link ProtocolToPartyConnFactory} that allows
-	 *                          the protocol to connect with the {@link Reference}s
-	 *                          in the settings
+	 * @param connectionfactory the {@link ProtocolToPartyConnFactory} that
+	 *                          allows the protocol to connect with the
+	 *                          {@link Reference}s in the settings
 	 */
 	void start(ProtocolToPartyConnFactory connectionfactory);
@@ -78,7 +87,7 @@
 	/**
 	 * 
-	 * @return a complete description of how this protocol behaves. Presented to the
-	 *         end users who should know negotiation basics but not all technical
-	 *         terms.
+	 * @return a complete description of how this protocol behaves. Presented to
+	 *         the end users who should know negotiation basics but not all
+	 *         technical terms.
 	 */
 	String getDescription();
@@ -86,6 +95,6 @@
 	/**
 	 * @return current state: the results of all sessions run so far and how to
-	 *         continue from that point. Changes over time as the session proceeds.
-	 *         Errors are also stored in the state.
+	 *         continue from that point. Changes over time as the session
+	 *         proceeds. Errors are also stored in the state.
 	 */
 	NegoState getState();
@@ -98,6 +107,7 @@
 
 	/**
-	 * Add a party after the protocol has started. Only some protocols can handle
-	 * this call. Usual protocols take the settings with their constructor.
+	 * Add a party after the protocol has started. Only some protocols can
+	 * handle this call. Usual protocols take the settings with their
+	 * constructor.
 	 * 
 	 * @param party the {@link PartyWithProfile} to be added.
Index: protocol/src/main/java/geniusweb/protocol/NegoState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/NegoState.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/NegoState.java	(revision 21)
@@ -5,13 +5,12 @@
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
 import geniusweb.protocol.session.SessionState;
 import geniusweb.protocol.tournament.TournamentState;
 
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-
 /**
- * The current state of the tournament. Must be serializabl;e so that it can be
- * restarted if a crash occurs.
+ * The current state of the session/tournament. Must be serializabl;e so that it
+ * can be restarted if a crash occurs.
  */
 @JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE)
@@ -36,9 +35,3 @@
 	boolean isFinal(long currentTimeMs);
 
-	/**
-	 * 
-	 * @return a fatal error occured during the tournament. returns null if no
-	 *         errors occured.
-	 */
-	ProtocolException getError();
 }
Index: protocol/src/main/java/geniusweb/protocol/ProtocolException.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/ProtocolException.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/ProtocolException.java	(revision 21)
@@ -2,5 +2,4 @@
 
 import geniusweb.actions.PartyId;
-import geniusweb.party.Party;
 import geniusweb.references.PartyRef;
 
@@ -10,5 +9,6 @@
  */
 public class ProtocolException extends Exception {
-	private String party; // the failing party
+	private static final long serialVersionUID = 4618901784366523980L;
+	private PartyId party; // the failing party
 
 	/**
@@ -17,5 +17,5 @@
 	 *                {@link PartyRef}
 	 */
-	public ProtocolException(String message, String party) {
+	public ProtocolException(String message, PartyId party) {
 		this(message, party, null);
 	}
@@ -30,9 +30,8 @@
 	 * 
 	 * @param message the error message
-	 * @param party   offending party, either the {@link PartyId} or a
-	 *                {@link PartyRef}
+	 * @param party   offending {@link PartyId}
 	 * @param e       the cause of the error
 	 */
-	public ProtocolException(String message, String party, Throwable e) {
+	public ProtocolException(String message, PartyId party, Throwable e) {
 		super(party + ":" + message, e, true, false);
 		this.party = party;
@@ -43,5 +42,5 @@
 	 * @return offending party, either the {@link PartyId} or a {@link PartyRef}
 	 */
-	public String getParty() {
+	public PartyId getParty() {
 		return party;
 	}
Index: protocol/src/main/java/geniusweb/protocol/WillBeCalled.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/WillBeCalled.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/WillBeCalled.java	(revision 21)
@@ -0,0 +1,46 @@
+package geniusweb.protocol;
+
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Object containing function that will be called exactly once, on demand or at
+ * given deadline.
+ *
+ */
+public class WillBeCalled {
+	private static volatile ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
+			2);
+	private final Runnable runnable;
+	private volatile AtomicBoolean done = new AtomicBoolean(false);
+
+	/**
+	 * @param r       the runnable that will be run once, either when the user
+	 *                calls {@link #complete(Void)} or after delayMs, whichever
+	 *                comes first.
+	 * @param delayMs the delay after this will be called anywa (the 'deadline'
+	 *                in milliseconds. If null, no deadline call will be made
+	 *                and the user must call {@link #complete(Void)} himself.
+	 */
+	public WillBeCalled(Runnable r, Long delayMs) {
+		this.runnable = r;
+		if (delayMs != null) {
+			executor.schedule(() -> complete(), delayMs, TimeUnit.MILLISECONDS);
+		}
+	}
+
+	/**
+	 * Tells the runnable to be executed. Only the first time the function is
+	 * run, subsequent calls do nothing. Failures in runnable are NOT caught
+	 * here, as they are probably bugs
+	 * 
+	 * We must avoid synchronized here because users may use synchronized blocks
+	 * already.
+	 */
+	public void complete() {
+		if (!done.compareAndSet(false, true))
+			return;
+		runnable.run();
+	}
+}
Index: protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConn.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConn.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConn.java	(revision 21)
@@ -4,5 +4,5 @@
 import geniusweb.actions.PartyId;
 import geniusweb.connection.ConnectionEnd;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 
 /**
@@ -14,5 +14,5 @@
 	/**
 	 * 
-	 * @return the partyId of the party that this connects to
+	 * @return the partyId of the party that this connects to.
 	 */
 	PartyId getParty();
Index: protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnFactory.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnFactory.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnFactory.java	(revision 21)
@@ -6,5 +6,5 @@
 import geniusweb.actions.Action;
 import geniusweb.connection.ConnectionFactory;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 import geniusweb.references.Reference;
 import tudelft.utilities.repository.NoResourcesNowException;
Index: protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnections.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnections.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/partyconnection/ProtocolToPartyConnections.java	(revision 21)
@@ -9,10 +9,11 @@
 
 import geniusweb.actions.PartyId;
-import geniusweb.party.inform.Inform;
+import geniusweb.inform.Inform;
 
 /**
  * Contains all parties with their connections. immutable
  */
-public class ProtocolToPartyConnections implements Iterable<ProtocolToPartyConn> {
+public class ProtocolToPartyConnections
+		implements Iterable<ProtocolToPartyConn> {
 	private List<ProtocolToPartyConn> connections;
 
Index: protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/DefaultSessionState.java	(revision 21)
@@ -57,5 +57,5 @@
 	 *                      {@link ProtocolException}s. All errors in our own
 	 *                      code are bugs (not ProtocolExceptions) and should
-	 *                      result in a throw.
+	 *                      result in a throw and terminate the session.
 	 */
 	public DefaultSessionState(List<Action> actions,
@@ -130,5 +130,4 @@
 	}
 
-	@Override
 	public ProtocolException getError() {
 		return error;
Index: protocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/OnePartyTeam.java	(revision 21)
@@ -0,0 +1,30 @@
+package geniusweb.protocol.session;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+
+/**
+ * Team that contains just 1 party.
+ */
+public class OnePartyTeam extends PartyWithProfile
+		implements TeamOfPartiesAndProfiles {
+
+	@JsonCreator
+	public OnePartyTeam(@JsonProperty("party") PartyWithParameters party,
+			@JsonProperty("profile") ProfileRef profile) {
+		super(party, profile);
+	}
+
+	@Override
+	public List<PartyWithProfile> getAllParties() {
+		return Arrays.asList(this);
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/SessionResult.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/SessionResult.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/SessionResult.java	(revision 21)
@@ -10,5 +10,5 @@
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.references.PartyWithProfile;
 
@@ -20,5 +20,5 @@
 public class SessionResult {
 	private final List<PartyWithProfile> participants;
-	private final Bid agreement;
+	private final Agreements agreements;
 	private final List<Double> penalties;
 
@@ -33,6 +33,5 @@
 	 *                     never be null. Some of them may have entered later of
 	 *                     left early. This list should contain them all.
-	 * @param agreement    the final agreement {@link Bid} of the session. Null
-	 *                     if no agreement was reached.
+	 * @param agreements   the final Agreements.
 	 * @param penalties    the penalties in [0,1] for each participant, same
 	 *                     order as the participants list.
@@ -48,9 +47,9 @@
 	public SessionResult(
 			@JsonProperty("participants") List<PartyWithProfile> participants,
-			@JsonProperty("agreement") Bid agreement,
+			@JsonProperty("agreements") Agreements agreements,
 			@JsonProperty("penalties") List<Double> penalties,
 			@JsonProperty("error") Throwable error) {
 		this.participants = participants;
-		this.agreement = agreement;
+		this.agreements = agreements;
 		this.penalties = penalties;
 		this.error = error;
@@ -69,9 +68,9 @@
 
 	/**
-	 * 8return the final agreement {@link Bid} of the session. Null if no
-	 * agreement was reached.
+	 * 8return the final {@link Agreements} of the session. May be empty, not
+	 * null
 	 */
-	public Bid getAgreement() {
-		return agreement;
+	public Agreements getAgreements() {
+		return agreements;
 	};
 
@@ -99,5 +98,5 @@
 	@Override
 	public String toString() {
-		return "SessionResult[" + participants + "," + agreement + ","
+		return "SessionResult[" + participants + "," + agreements + ","
 				+ penalties + "," + error + "]";
 	}
@@ -108,5 +107,5 @@
 		int result = 1;
 		result = prime * result
-				+ ((agreement == null) ? 0 : agreement.hashCode());
+				+ ((agreements == null) ? 0 : agreements.hashCode());
 		result = prime * result + ((error == null) ? 0 : error.hashCode());
 		result = prime * result
@@ -126,8 +125,8 @@
 			return false;
 		SessionResult other = (SessionResult) obj;
-		if (agreement == null) {
-			if (other.agreement != null)
+		if (agreements == null) {
+			if (other.agreements != null)
 				return false;
-		} else if (!agreement.equals(other.agreement))
+		} else if (!agreements.equals(other.agreements))
 			return false;
 		if (error == null) {
Index: protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/SessionSettings.java	(revision 21)
@@ -6,4 +6,6 @@
 
 import geniusweb.protocol.NegoSettings;
+import geniusweb.protocol.session.amop.AMOPSettings;
+import geniusweb.protocol.session.mopac.MOPACSettings;
 import geniusweb.protocol.session.saop.SAOPSettings;
 import geniusweb.protocol.session.shaop.SHAOPSettings;
@@ -15,5 +17,7 @@
  */
 @JsonSubTypes({ @JsonSubTypes.Type(value = SAOPSettings.class),
-		@JsonSubTypes.Type(value = SHAOPSettings.class) })
+		@JsonSubTypes.Type(value = SHAOPSettings.class),
+		@JsonSubTypes.Type(value = AMOPSettings.class),
+		@JsonSubTypes.Type(value = MOPACSettings.class) })
 public interface SessionSettings extends NegoSettings {
 
Index: protocol/src/main/java/geniusweb/protocol/session/SessionState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/SessionState.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/SessionState.java	(revision 21)
@@ -6,7 +6,9 @@
 
 import geniusweb.actions.Action;
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.progress.Progress;
 import geniusweb.protocol.NegoState;
+import geniusweb.protocol.session.amop.AMOPState;
+import geniusweb.protocol.session.mopac.MOPACState;
 import geniusweb.protocol.session.saop.SAOPState;
 import geniusweb.protocol.session.shaop.SHAOPState;
@@ -35,6 +37,7 @@
  */
 @JsonSubTypes({ @JsonSubTypes.Type(value = SAOPState.class),
-		@JsonSubTypes.Type(value = SHAOPState.class) })
-
+		@JsonSubTypes.Type(value = SHAOPState.class),
+		@JsonSubTypes.Type(value = AMOPState.class),
+		@JsonSubTypes.Type(value = MOPACState.class) })
 public interface SessionState extends NegoState {
 
@@ -45,5 +48,6 @@
 	 * 
 	 * @return unmodifyable list of actions done so far, in the order in which
-	 *         they arrived.
+	 *         they arrived. List of list allows implementations to add some
+	 *         extra structure to the actions, eg one list per phase
 	 */
 	List<Action> getActions();
@@ -64,5 +68,5 @@
 	 *         {@link #isFinal()}.
 	 */
-	Bid getAgreement();
+	Agreements getAgreements();
 
 	/**
Index: protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/TeamOfPartiesAndProfiles.java	(revision 21)
@@ -7,5 +7,4 @@
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 
-import geniusweb.protocol.session.saop.SaopPartyWithProfile;
 import geniusweb.protocol.session.shaop.ShaopTeam;
 import geniusweb.references.PartyWithProfile;
@@ -23,5 +22,5 @@
 // and do not (de)serialize individual TeamOfPartiesAndProfiles.
 //@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
-@JsonSubTypes({ @JsonSubTypes.Type(value = SaopPartyWithProfile.class),
+@JsonSubTypes({ @JsonSubTypes.Type(value = OnePartyTeam.class),
 		@JsonSubTypes.Type(value = ShaopTeam.class) })
 public interface TeamOfPartiesAndProfiles {
Index: protocol/src/main/java/geniusweb/protocol/session/amop/AMOP.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/amop/AMOP.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/amop/AMOP.java	(revision 21)
@@ -0,0 +1,434 @@
+package geniusweb.protocol.session.amop;
+
+import java.io.IOException;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.deadline.Deadline;
+import geniusweb.events.ProtocolEvent;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.Voting;
+import geniusweb.inform.YourTurn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.ProgressFactory;
+import geniusweb.protocol.CurrentNegoState;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.WillBeCalled;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.amop.AMOPState.Phase;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import geniusweb.references.ProtocolRef;
+import geniusweb.references.Reference;
+import tudelft.utilities.listener.DefaultListenable;
+import tudelft.utilities.logging.Reporter;
+import tudelft.utilities.repository.NoResourcesNowException;
+
+/**
+ * 
+ * All parties are first sent the {@link SessionSettings}. Only parties
+ * specified initially in the settings do participate.
+ * 
+ * <h1>parameter</h1> AMOP parties can receive a parameter: minVotes containing
+ * a Double. If set, the AMOP protocol checks that all {@link Votes} from that
+ * party have {@link Vote#getMinPower()} > minVotes.
+ * <h1>Protocol steps</h1> The protocol tries to start all parties. If not all
+ * parties start, the parties are freed up and another attempt is done to start
+ * all parties some time later.
+ * <li>the variable remainingparties = {all parties}
+ * <li>The session deadline clock now starts ticking.
+ * <li>All parties are sent their settings.
+ * <li>Loop until {@link Deadline} is reached or |remainingparties|<2:
+ * <ol>
+ * <li>protocol sends {@link YourTurn} to all remainingparties. Each party now
+ * must submit an {@link Offer} within {@link #PHASE_TIME} seconds. If a party
+ * fails to submit it is send Finished and removed from from remainingparties.
+ * <li>protocol sends a {@link Voting} containing a List<Bid> containing all
+ * received {@link Bid}s. Each party must place his {@link Votes} within
+ * {@link #PHASE_TIME} seconds. If a party does not submit, it is send a
+ * {@link Finished} and removed from remainingparties. Previous votes for the
+ * same bid do not count. But see {@link Agreements}.
+ * <li>The protocol determines a maximum sized subset of size N>=2 of the votes
+ * for bid B for which the vote conditions hold. If there is such a subset, the
+ * parties that placed these votes reached an agreement. They are added to the
+ * agreement set, sent the {@link Finished} info and terminated. They are
+ * removed from the remainingparties.
+ * </ol>
+ * <li>Any remaining parties are sent a {@link Finished} object without
+ * agreement bid and are terminated.
+ * </ol>
+ * <p>
+ * A party can send {@link EndNegotiation} at any time to remove itself from the
+ * negotiation. The other parties may continue anyawy without him.
+ * <p>
+ * Parties receive the {@link Finished} information at the very end when all
+ * agreements have been collected.
+ * <p>
+ * This logs to "Protocol" logger if there are issues
+ * <p>
+ * This object is mutable: the internal state changes as parties interact with
+ * the protocol.
+ * <p>
+ * Threadsafe: all entrypoints are synhronized.
+ */
+public class AMOP extends DefaultListenable<ProtocolEvent>
+		implements SessionProtocol {
+	private static final long PHASE_TIME = 30000; // millis
+	private static final long TIME_MARGIN = 20l;// ms extra delay after deadline
+	private static final int MINDURATION = 100;
+	private static final int MIN_SLEEP_TIME = 1000;
+	private static final int MAX_SLEEP_TIME = 60000;
+	private static final ProtocolRef AMOP = new ProtocolRef("AMOP");
+
+	private final Reporter log;
+	private AMOPState state = null; // mutable!
+	private volatile WillBeCalled finish = new WillBeCalled(() -> finish(),
+			null);
+	private volatile WillBeCalled endPhase = new WillBeCalled(() -> endPhase(),
+			null);
+
+	/**
+	 * 
+	 * @param state normally the initial state coming from SAOPSettings
+	 */
+	public AMOP(AMOPState state, Reporter logger) {
+		if (state == null) {
+			throw new NullPointerException("state must be not null");
+		}
+		if (state.getSettings().getDeadline().getDuration() < MINDURATION) {
+			throw new IllegalArgumentException(
+					"Duration must be at least " + MINDURATION);
+		}
+		if (logger == null) {
+			throw new NullPointerException("Logger must be not null");
+		}
+		this.log = logger;
+		this.state = state;
+	}
+
+	@Override
+	public synchronized void start(
+			ProtocolToPartyConnFactory connectionfactory) {
+		try {
+			// we're in Phase.INIT still
+			connect(connectionfactory);
+			// this moves to
+			setDeadline();
+			setupParties();
+			endPhase.complete(); // end of INIT
+		} catch (Throwable e) {
+			handleError("Failed to start up session", new PartyId("UNKNOWN"),
+					e);
+		}
+	}
+
+	@Override
+	public String getDescription() {
+		return "All parties get YourTurn. They now can submit their Offer within 30 seconds. "
+				+ "Next they receive a ElicitComparison. They can now submit their Votes."
+				+ "If one of their Vote succeeds, they finish with an agreement. "
+				+ "Parties that did not yet reach agreement continue negotiating. "
+				+ "A parameter 'minVotes', if set, contains the the smallest value to be used in each Vote";
+	}
+
+	@Override
+	public void addParticipant(PartyWithProfile party)
+			throws IllegalStateException {
+		throw new IllegalStateException(
+				"Dynamic joining a negotiation is not supported in AMOP");
+	}
+
+	@Override
+	public AMOPState getState() {
+		return state;
+	}
+
+	@Override
+	public ProtocolRef getRef() {
+		return AMOP;
+	}
+
+	/*******************************************************************
+	 * private functions. Some are protected only, for testing purposes
+	 ********************************************************************/
+	/**
+	 * step 1 in protocol: connect all involved parties and start the clock.
+	 * This always "succeeds" with a valid (but possibly final) state
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @param connectionfactory the connectionfactory for making party
+	 *                          connections
+	 * 
+	 * @throws InterruptedException if the connection procedure is unterrupted
+	 * 
+	 * @throws IOException          if this fails to properly conect to the
+	 *                              parties, eg interrupted or server not
+	 *                              responding..
+	 */
+	protected synchronized void connect(
+			ProtocolToPartyConnFactory connectionfactory)
+			throws InterruptedException, IOException {
+		List<PartyWithProfile> participants = state.getSettings()
+				.getAllParties();
+		List<Reference> parties = participants.stream()
+				.map(parti -> (parti.getParty().getPartyRef()))
+				.collect(Collectors.toList());
+		List<ProtocolToPartyConn> connections = null;
+		log.log(Level.INFO, "AMOP connect " + parties);
+		while (connections == null) {
+			try {
+				connections = connectionfactory.connect(parties);
+			} catch (NoResourcesNowException e) {
+				long waitms = e.getLater().getTime()
+						- System.currentTimeMillis();
+				log.log(Level.INFO,
+						"No resources available to run session, waiting"
+								+ waitms);
+				Thread.sleep(Math.min(MAX_SLEEP_TIME,
+						Math.max(MIN_SLEEP_TIME, waitms)));
+			}
+		}
+		for (int i = 0; i < participants.size(); i++) {
+			setState(this.state.with(connections.get(i), participants.get(i)));
+		}
+	}
+
+	/**
+	 * overridable factory method, used for testing.
+	 * 
+	 * @param r
+	 * @param delayMs
+	 * @return new WillBeCalled(r,delayms)
+	 */
+	protected WillBeCalled createWillBeCalled(Runnable r, Long delayMs) {
+		return new WillBeCalled(r, delayMs);
+	}
+
+	/**
+	 * Set state to proper deadline. Starts the timer tasks. This tasks triggers
+	 * a call to handleError when the session times out.
+	 */
+	private void setDeadline() {
+		long now = System.currentTimeMillis();
+		Deadline deadline = state.getSettings().getDeadline();
+		setState(state.with(ProgressFactory.create(deadline, now)));
+
+		finish = createWillBeCalled(() -> finish(),
+				TIME_MARGIN + deadline.getDuration());
+		log.log(Level.INFO, "AMOP deadline set to "
+				+ new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
+						.format(new Date(System.currentTimeMillis()
+								+ deadline.getDuration() + TIME_MARGIN)));
+	}
+
+	/**
+	 * step 2 in protocol: listen to connections and send settings to the
+	 * parties.
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @throws ProtocolException if a party does not follow the protocol
+	 */
+	protected synchronized void setupParties() throws ProtocolException {
+		for (ProtocolToPartyConn conn : state.getConnections()) {
+			conn.addListener(action -> actionRequest(conn, action));
+		}
+
+		for (ProtocolToPartyConn connection : state.getConnections()) {
+			try {
+				sendSettings(connection);
+			} catch (IOException e) {
+				throw new ProtocolException("Failed to initialize",
+						connection.getParty(), e);
+			}
+		}
+
+	}
+
+	/**
+	 * Inform a party about its settings
+	 * 
+	 * @param connection
+	 * @throws IOException if party got disconnected
+	 */
+	private synchronized void sendSettings(ProtocolToPartyConn connection)
+			throws IOException {
+		PartyId partyid = connection.getParty();
+		ProfileRef profile = state.getPartyProfiles().get(partyid).getProfile();
+		Parameters params = state.getPartyProfiles().get(partyid).getParty()
+				.getParameters();
+		if (profile == null) {
+			throw new IllegalArgumentException(
+					"Missing profile for party " + connection.getReference());
+		}
+		connection.send(new Settings(connection.getParty(), profile, getRef(),
+				state.getProgress(), params));
+	}
+
+	/**
+	 * This is called when one of the {@link ProtocolToPartyConn}s does an
+	 * action. Synchronized so that we always handle only 1 action at a time.
+	 * 
+	 * @param partyconn the connection on which the action came in
+	 * @param action    the {@link Action} taken by some party
+	 */
+	protected synchronized void actionRequest(
+			final ProtocolToPartyConn partyconn, final Action action) {
+		if (action == null) {
+			Throwable err = partyconn.getError();
+			if (err == null) {
+				err = new ProtocolException("Party sent a null action",
+						partyconn.getParty());
+			}
+			handleError(partyconn + "Protocol error", partyconn.getParty(),
+					err);
+			return;
+		}
+
+		try {
+			setState(state.with(partyconn.getParty(), action));
+			state.getConnections().broadcast(new ActionDone(action));
+			if (state.isAllPartiesActed()) {
+				endPhase.complete();
+			}
+		} catch (Throwable e) {
+			handleError("failed to handle action " + action,
+					partyconn.getParty(), e);
+		}
+
+	}
+
+	/**
+	 * The current phase is completed. Proceed to next phase as needed. Reset
+	 * the deadline timers and inform parties. Increase progress if necessary.
+	 * Must only be called through {@link #endPhase} to ensure this is called
+	 * only once.
+	 */
+	private synchronized void endPhase() {
+		if (state.isFinal(System.currentTimeMillis()))
+			return;
+
+		List<Bid> bids = null;
+		if (state.getPhase() == Phase.OFFER) {
+			bids = state.getPhaseActions().values().stream()
+					.map(act -> ((Offer) act).getBid())
+					.collect(Collectors.toList());
+		}
+
+		setState(state.nextPhase());
+
+		if (state.getPhase() == Phase.OFFER) {
+			broadcast(new YourTurn());
+		} else {
+			Map<PartyId, Integer> powers = state.getActiveParties().stream()
+					.collect(Collectors.toMap(pid -> pid, pid -> 1));
+			broadcast(new Voting(bids, powers));
+		}
+		/**
+		 * Really tricky but we can safely replace {@link #endPhase}: all
+		 * functions are synchronized so the state is always consistent when
+		 * this is called. MNMMM Yes and then?? need proof that not called right
+		 * after replace. I think we need proof that #endPhase is always
+		 * consistent with state , that phase in state is coupled
+		 */
+		endPhase = createWillBeCalled(() -> endPhase(),
+				PHASE_TIME + TIME_MARGIN);
+	}
+
+	/**
+	 * Alternative to {@link ProtocolToPartyConnections#broadcast(Inform)},
+	 * sending only to active parties
+	 * 
+	 * @param info the info to broadcast
+	 */
+	private void broadcast(Inform info) {
+		for (PartyId party : state.getActiveParties()) {
+			try {
+				state.getConnections().get(party).send(info);
+			} catch (IOException e) {
+				handleError("Party seems to have disconnected", party, e);
+			}
+		}
+	}
+
+	/**
+	 * Update state to include the given error and closes the party.
+	 * 
+	 * @param message The message to attach to the error
+	 * @param party   the party where the error occured
+	 * @param e       the exception that occured.
+	 */
+	private synchronized void handleError(final String message,
+			final PartyId partyid, final Throwable e) {
+		if (e instanceof ProtocolException) {
+			setState(state.with(partyid, (ProtocolException) e));
+		} else {
+			setState(state.with(partyid,
+					new ProtocolException(message, partyid, e)));
+		}
+		log.log(Level.WARNING,
+				"AMOP intercepted error from party " + partyid + ":" + message,
+				e);
+	}
+
+	/**
+	 * Sets the new state. If the new state is final, the finish-up procedure is
+	 * executed. All state changes go through here to ensure we check isFinal
+	 * before any state change.
+	 * 
+	 * @param newstate the new state.
+	 */
+	private synchronized void setState(AMOPState newstate) {
+		long now = System.currentTimeMillis();
+		if (state.isFinal(now)) {
+			finish.complete();
+			return;
+		}
+		this.state = newstate;
+		if (newstate.isFinal(now)) {
+			finish.complete();
+		}
+	}
+
+	/**
+	 * Called when we reach final state. Must be called through {@link #finish}
+	 * to ensure this is called only once. Send finished info to all parties,
+	 * notify current nego state as final and set {@link #isFinishedInfoSent}.
+	 * Double calls are automatically ignored.
+	 */
+	private synchronized void finish() {
+		Finished finished = new Finished(state.getAgreements());
+		for (ProtocolToPartyConn conn : state.getConnections()) {
+			try {
+				conn.send(finished);
+				conn.close();
+			} catch (Exception e) {
+				log.log(Level.INFO, "Failed to send Finished to " + conn, e);
+			}
+		}
+		notifyListeners(new CurrentNegoState(state));
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/amop/AMOPSettings.java	(revision 21)
@@ -0,0 +1,127 @@
+package geniusweb.protocol.session.amop;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.deadline.Deadline;
+import geniusweb.protocol.session.OnePartyTeam;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.PartyWithProfile;
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * Settings for AMOP negotiation.
+ */
+public class AMOPSettings implements SessionSettings {
+	private final List<OnePartyTeam> participants;
+	private final Deadline deadline;
+
+	/**
+	 * 
+	 * @param participants the list of {@link PartyWithProfile} in clockwise
+	 *                     order. There must be at least 2 to run the SAOP
+	 *                     protocol. But SAOP can be initialized with less, for
+	 *                     use in TournamentSettings.
+	 * @param deadline
+	 * @param minimumVotes the minimum number of votes allowed for a bid.
+	 */
+	@JsonCreator
+	public AMOPSettings(
+			@JsonProperty("participants") List<OnePartyTeam> participants,
+			@JsonProperty("deadline") Deadline deadline) {
+		this.participants = participants;
+		this.deadline = deadline;
+		if (participants == null) {
+			throw new IllegalArgumentException("participants must not be null");
+		}
+		if (deadline == null) {
+			throw new IllegalArgumentException("deadline must not be null");
+		}
+	}
+
+	@Override
+	public Double getMaxRunTime() {
+		return deadline.getDuration() / 1000d;
+	}
+
+	@Override
+	public SessionProtocol getProtocol(Reporter logger) {
+		return new AMOP(new AMOPState(this), logger);
+	}
+
+	@Override
+	public List<TeamOfPartiesAndProfiles> getTeams() {
+		return Collections.unmodifiableList(participants);
+	}
+
+	/**
+	 * @return the deadline for this negotiation
+	 */
+	public Deadline getDeadline() {
+		return deadline;
+	}
+
+	@Override
+	public List<PartyWithProfile> getAllParties() {
+		return participants.stream()
+				.map(particip -> particip.getAllParties().get(0))
+				.collect(Collectors.toList());
+	}
+
+	@Override
+	public String toString() {
+		return "AMOPSettings" + participants + "," + deadline + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((deadline == null) ? 0 : deadline.hashCode());
+		result = prime * result
+				+ ((participants == null) ? 0 : participants.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		AMOPSettings other = (AMOPSettings) obj;
+		if (deadline == null) {
+			if (other.deadline != null)
+				return false;
+		} else if (!deadline.equals(other.deadline))
+			return false;
+		if (participants == null) {
+			if (other.participants != null)
+				return false;
+		} else if (!participants.equals(other.participants))
+			return false;
+		return true;
+	}
+
+	@Override
+	public SessionSettings with(TeamOfPartiesAndProfiles party) {
+		if (!(party instanceof OnePartyTeam))
+			throw new IllegalArgumentException(
+					"Added party must be AmopPartyWithProfile but got "
+							+ party);
+		List<OnePartyTeam> newparts = new LinkedList<>(participants);
+		newparts.add((OnePartyTeam) party);
+		return new AMOPSettings(newparts, deadline);
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/amop/AMOPState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/amop/AMOPState.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/amop/AMOPState.java	(revision 21)
@@ -0,0 +1,456 @@
+package geniusweb.protocol.session.amop;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.YourTurn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionResult;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.voting.CollectedVotes;
+
+public class AMOPState implements SessionState {
+	/**
+	 * Phase determines what actions are allowed now. #isFinal terminates the
+	 * state.
+	 */
+	public enum Phase {
+		INIT, OFFER, VOTE;
+
+		public Phase next() {
+			return this == OFFER ? VOTE : OFFER;
+		}
+	}
+
+	private final Phase phase;
+	private final AMOPSettings settings;
+	private final Map<PartyId, PartyWithProfile> partyprofiles;
+	private final ProtocolToPartyConnections connections;
+	private final Agreements agreements;
+	private final List<PartyId> walkedAway;
+	private final List<Action> actions;
+	private final Progress progress;
+	private final Map<PartyId, ProtocolException> exceptions;
+
+	/**
+	 * Creates the initial state from the given settings and progress=null
+	 * 
+	 * @param settings the {@link SAOPSettings}
+	 */
+	public AMOPState(AMOPSettings settings) {
+		this(Phase.INIT, Collections.emptyList(),
+				new ProtocolToPartyConnections(Collections.emptyList()), null,
+				settings, Collections.emptyMap(), new Agreements(),
+				Collections.emptyMap(), Collections.emptyList());
+
+	}
+
+	/**
+	 * @param phase         The Phase
+	 * @param actions       the legal actions that have been done in the
+	 *                      negotiation. first action is the oldest. This MUST
+	 *                      NOT contain illegal actions, otherwise we can not
+	 *                      decypher the actions list anymore and find the
+	 *                      proper phase boundaries. Instead parties doing
+	 *                      illegal actions must be killed.
+	 * @param conns         the existing party connections. we assume ownership
+	 *                      of this so it should not be modified although
+	 *                      connections may of course break.
+	 * @param progress      the {@link Progress} line. can be null if not yet
+	 *                      known
+	 * @param settings      the {@link SAOPSettings}
+	 * @param partyprofiles map with the {@link PartyWithProfile} for connected
+	 *                      parties. null is equivalent to an empty map.
+	 * @param agreements    the agreements reached.
+	 * @param e             Possibly empty map of {@link ProtocolException}, the
+	 *                      keys are the party that failed to follow the
+	 *                      protocol.
+	 * @param walkedAway    list of parties that walked away from the
+	 *                      negotiation.
+	 */
+	protected AMOPState(Phase phase, List<Action> actions,
+			ProtocolToPartyConnections conns, Progress progress,
+			AMOPSettings settings, Map<PartyId, PartyWithProfile> partyprofiles,
+			Agreements agreements, Map<PartyId, ProtocolException> e,
+			List<PartyId> walkedAway) {
+		this.phase = phase;
+		this.actions = actions;
+		this.connections = conns;
+		this.progress = progress;
+		this.settings = settings;
+		this.partyprofiles = partyprofiles;
+		this.agreements = agreements;
+		this.exceptions = e;
+		this.walkedAway = walkedAway;
+	}
+
+	@Override
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	@Override
+	public Progress getProgress() {
+		return progress;
+	}
+
+	public Map<PartyId, PartyWithProfile> getPartyProfiles() {
+		return Collections.unmodifiableMap(partyprofiles);
+	}
+
+	/**
+	 * @return all current/remaining active party connections. Finished/crashed
+	 *         parties should be removed immediately.
+	 */
+	public ProtocolToPartyConnections getConnections() {
+		return connections;
+	}
+
+	/**
+	 * 
+	 * @param connection   the new {@link ProtocolToPartyConn}
+	 * @param partyprofile the {@link PartyWithProfile} that is associated with
+	 *                     this state
+	 * @return new SessionState with the new connection added. This call ignores
+	 *         the progress (does not check isFinal) because we uses this during
+	 *         the setup where the deadline is not yet relevant.
+	 */
+	protected AMOPState with(ProtocolToPartyConn connection,
+			PartyWithProfile partyprofile) {
+		ProtocolToPartyConnections newconns = getConnections().with(connection);
+		Map<PartyId, PartyWithProfile> newprofiles = new HashMap<>(
+				partyprofiles);
+		newprofiles.put(connection.getParty(), partyprofile);
+		return new AMOPState(Phase.INIT, actions, newconns, progress, settings,
+				newprofiles, agreements, exceptions, walkedAway);
+	}
+
+	/**
+	 * @param id the {@link PartyId} of the party that failed.
+	 * @param e  the {@link ProtocolException} that occured in the party
+	 * @return a new state with the error set.
+	 */
+	public AMOPState with(PartyId id, ProtocolException e) {
+		Map<PartyId, ProtocolException> newExc = exceptions;
+		if (!exceptions.containsKey(id)) {
+			newExc = new HashMap<>(exceptions);
+			newExc.put(id, e);
+		}
+		return new AMOPState(phase, actions, connections, progress, settings,
+				partyprofiles, agreements, newExc, walkedAway);
+	}
+
+	/**
+	 * Sets the progress for this session. Can be set only if progress=null.
+	 * Should be set in INIT phase.
+	 * 
+	 * @param newprogress the new progress
+	 * @return new SAOPState with the progress set
+	 */
+	public AMOPState with(Progress newprogress) {
+		if (progress != null || newprogress == null || phase != Phase.INIT) {
+			throw new IllegalArgumentException(
+					"progress must be null, newprogress must be not null and phase must be INIT");
+		}
+		return new AMOPState(phase, actions, connections, newprogress,
+				getSettings(), partyprofiles, agreements, exceptions,
+				walkedAway);
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		return agreements;
+	}
+
+	@Override
+	public boolean isFinal(long currentTimeMs) {
+		boolean pastDeadline = progress != null
+				&& progress.isPastDeadline(currentTimeMs);
+		return phase != Phase.INIT
+				&& (pastDeadline || getActiveParties().size() < 2);
+	}
+
+	@Override
+	public AMOPSettings getSettings() {
+		return settings;
+	}
+
+	/**
+	 * @param actor         the actor that did this action. Can be used to check
+	 *                      if action is valid. NOTICE caller has to make sure
+	 *                      the current state is not final. MUST NOT be null.
+	 * @param action        the action that was proposed by actor. MUST NOT be
+	 *                      null.
+	 * @param currentTimeMs the current time in ms since 1970, see
+	 *                      {@link System#currentTimeMillis()}
+	 * @return new SessionState with the action added as last action.
+	 * @throws ProtocolException if actor is violating the protocol
+	 * @throws RuntimeException  if we hit a bug.
+	 */
+
+	public AMOPState with(PartyId actor, Action action)
+			throws ProtocolException {
+		if (!actor.equals(action.getActor())) {
+			throw new ProtocolException(
+					"act by " + actor + " contains wrong actorid: " + action,
+					actor);
+		}
+		if (!getActiveParties().contains(actor))
+			throw new ProtocolException("Deactivated actor tried to act",
+					actor);
+		if (getPhaseActions().containsKey(actor))
+			throw new ProtocolException(
+					"Attempt to act twice in phase:" + action, actor);
+
+		List<Action> newactions = new LinkedList<>(getActions());
+		newactions.add(action);
+		List<PartyId> newWalkedAway = walkedAway;
+
+		// check protocol is followed for specific actions
+		if (action instanceof Votes) {
+			if (phase != Phase.VOTE)
+				throw new ProtocolException(
+						"Vote can only be placed in VOTE phase", actor);
+			// Notice we don't check the votes. You can actually vote for
+			// other bids. But such action would be useless as others can't vote
+			// on it.
+		} else if (action instanceof Offer) {
+			if (phase != Phase.OFFER) {
+				throw new ProtocolException(
+						"Offer can only be placed in OFFER phase", actor);
+			}
+		} else if (action instanceof EndNegotiation) {
+			newWalkedAway = new LinkedList<>(walkedAway);
+			newWalkedAway.add(action.getActor());
+
+		} else {
+			throw new ProtocolException(
+					"Action " + action + " is not allowed in AMOP", actor);
+		}
+
+		return new AMOPState(phase, newactions, connections, progress, settings,
+				partyprofiles, agreements, exceptions, newWalkedAway);
+	}
+
+	@Override
+	public SessionResult getResult() {
+		List<Double> emptylist = new LinkedList<>();
+		for (int n = 0; n < getConnections().size(); n++)
+			emptylist.add(0d);
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
+				emptylist, null);
+	}
+
+	/**
+	 * @return true iff all parties acted as required in the current Phase. We
+	 *         search back until the last {@link YourTurn} if Phase=VOTE. /
+	 *         {@link Vote} if Phase=OFFER and check that all current
+	 *         connections have acted.
+	 */
+	public boolean isAllPartiesActed() {
+		return getPhaseActions().keySet().containsAll(getActiveParties());
+	}
+
+	/**
+	 * @return all actions of the current phase NOTE this assumes at least 1
+	 *         action is done in each phase, so that we can detect the previous
+	 *         phase actions in the actions list.
+	 * 
+	 */
+	public Map<PartyId, Action> getPhaseActions() {
+		Map<PartyId, Action> newactions = new HashMap<>();
+		for (int n = actions.size() - 1; n >= 0; n--) {
+			Action act = actions.get(n);
+			if (act instanceof EndNegotiation)
+				continue;
+			if (phase == Phase.VOTE && !(act instanceof Votes))
+				break;
+			if (phase == Phase.OFFER && !(act instanceof Offer))
+				break;
+			newactions.put(act.getActor(), act);
+		}
+		return newactions;
+
+	}
+
+	/**
+	 * 
+	 * @return all currently active parties. These are the parties that do not
+	 *         yet have an {@link #agreements} nor caused {@link #exceptions}
+	 *         nor {@link #walkedAway}
+	 */
+	public List<PartyId> getActiveParties() {
+		List<PartyId> active = connections.stream().map(conn -> conn.getParty())
+				.collect(Collectors.toList());
+		active.removeAll(agreements.getMap().keySet());
+		active.removeAll(exceptions.keySet());
+		active.removeAll(walkedAway);
+		return active;
+	}
+
+	public Phase getPhase() {
+		return phase;
+	}
+
+	/**
+	 * 
+	 * @return list of parties that walked away with {@link EndNegotiation}
+	 */
+	public List<PartyId> getWalkedAway() {
+		return walkedAway;
+	}
+
+	/**
+	 * 
+	 * @return new state with next phase selected and a updated list of
+	 *         agreements. This must be called to end a phase, it's not
+	 *         triggered by an action. If current phase is VOTE and next phase
+	 *         OFFER then progress is also incremented.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public AMOPState nextPhase() {
+		Agreements newagreements = agreements;
+		Progress newprogress = progress;
+		if (phase == Phase.VOTE) {
+			// hacky cast
+			newagreements = collectVotes((Map) getPhaseActions());
+			if (newprogress instanceof ProgressRounds) {
+				newprogress = ((ProgressRounds) newprogress).advance();
+			}
+		}
+		return new AMOPState(phase.next(), actions, connections, newprogress,
+				settings, partyprofiles, newagreements, exceptions, walkedAway);
+	}
+
+	/**
+	 * 
+	 * @param allvotes the Votes of each party
+	 * @return all agreements that were reached from the given votes
+	 */
+	protected Agreements collectVotes(Map<PartyId, Votes> allvotes) {
+		Agreements newagreements = agreements;
+		while (true) {
+			// power is 1 for each known party.
+			Map<PartyId, Integer> powers = allvotes.keySet().stream()
+					.collect(Collectors.toMap(p -> p, p -> 1));
+			Map<Bid, Set<PartyId>> agrees = new CollectedVotes(allvotes, powers)
+					.getMaxAgreements();
+			if (agrees.isEmpty())
+				break;
+			// find the best one
+			Bid maxbid = agrees.keySet().stream()
+					.max(Comparator.comparingInt(bid -> agrees.get(bid).size()))
+					.get();
+			newagreements = newagreements.with(maxbid, agrees.get(maxbid));
+			for (PartyId party : agrees.get(maxbid)) {
+				allvotes.remove(party);
+			}
+		}
+		return newagreements;
+	}
+
+	@Override
+	public String toString() {
+		return "AMOPState[" + phase + "," + settings + "," + partyprofiles + ","
+				+ connections + "," + agreements + "," + walkedAway + ","
+				+ actions + "," + progress + "," + exceptions + "," + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((actions == null) ? 0 : actions.hashCode());
+		result = prime * result
+				+ ((agreements == null) ? 0 : agreements.hashCode());
+		result = prime * result
+				+ ((connections == null) ? 0 : connections.hashCode());
+		result = prime * result
+				+ ((exceptions == null) ? 0 : exceptions.hashCode());
+		result = prime * result
+				+ ((partyprofiles == null) ? 0 : partyprofiles.hashCode());
+		result = prime * result + ((phase == null) ? 0 : phase.hashCode());
+		result = prime * result
+				+ ((progress == null) ? 0 : progress.hashCode());
+		result = prime * result
+				+ ((settings == null) ? 0 : settings.hashCode());
+		result = prime * result
+				+ ((walkedAway == null) ? 0 : walkedAway.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		AMOPState other = (AMOPState) obj;
+		if (actions == null) {
+			if (other.actions != null)
+				return false;
+		} else if (!actions.equals(other.actions))
+			return false;
+		if (agreements == null) {
+			if (other.agreements != null)
+				return false;
+		} else if (!agreements.equals(other.agreements))
+			return false;
+		if (connections == null) {
+			if (other.connections != null)
+				return false;
+		} else if (!connections.equals(other.connections))
+			return false;
+		if (exceptions == null) {
+			if (other.exceptions != null)
+				return false;
+		} else if (!exceptions.equals(other.exceptions))
+			return false;
+		if (partyprofiles == null) {
+			if (other.partyprofiles != null)
+				return false;
+		} else if (!partyprofiles.equals(other.partyprofiles))
+			return false;
+		if (phase != other.phase)
+			return false;
+		if (progress == null) {
+			if (other.progress != null)
+				return false;
+		} else if (!progress.equals(other.progress))
+			return false;
+		if (settings == null) {
+			if (other.settings != null)
+				return false;
+		} else if (!settings.equals(other.settings))
+			return false;
+		if (walkedAway == null) {
+			if (other.walkedAway != null)
+				return false;
+		} else if (!walkedAway.equals(other.walkedAway))
+			return false;
+		return true;
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/MOPAC.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/MOPAC.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/MOPAC.java	(revision 21)
@@ -0,0 +1,424 @@
+package geniusweb.protocol.session.mopac;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.deadline.Deadline;
+import geniusweb.events.ProtocolEvent;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.Voting;
+import geniusweb.inform.YourTurn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.ProgressFactory;
+import geniusweb.protocol.CurrentNegoState;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionProtocol;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.mopac.phase.Phase;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.references.ProfileRef;
+import geniusweb.references.ProtocolRef;
+import geniusweb.references.Reference;
+import tudelft.utilities.listener.DefaultListenable;
+import tudelft.utilities.logging.Reporter;
+import tudelft.utilities.repository.NoResourcesNowException;
+
+/**
+ * 
+ * All parties are first sent the {@link SessionSettings}. Only parties
+ * specified initially in the settings do participate (no late joining in)
+ * 
+ * <h1>parameter</h1> MOPAC parties can receive a parameter <code>power</code>
+ * containing a Integer. This parameter is picked up by this protocol, for
+ * handling the agreement extraction from the offers. Power=1 for parties that
+ * do not have the power parameter.
+ * <h1>Protocol steps</h1>
+ * 
+ * <ol>
+ * <li>The protocol tries to start all parties. If not all parties start, the
+ * parties are freed up and another attempt is done to start all parties some
+ * time later.
+ * <li>the variable remainingparties = {all parties}
+ * <li>The session deadline clock now starts ticking.
+ * <li>remainingparties are sent their settings.
+ * <li>Loop until {@link Deadline} is reached or |remainingparties|<2:
+ * <ol>
+ * <li>protocol sends {@link YourTurn} to all remainingparties. Each party now
+ * must submit an {@link Offer} within {@link Phase#PHASE_MAXTIME} seconds. If a
+ * party fails to submit it is removed from from remainingparties.
+ * <li>protocol sends a {@link Voting} containing a List<Bid> containing all
+ * received {@link Bid}s. Each party must place his {@link Votes} within
+ * {@link #PHASE_TIME} seconds. If a party does not submit, it is terminated and
+ * removed from remainingparties. Previous votes for the same bid do not count.
+ * But see {@link Agreements}. A party that misbehaves after submitting its vote
+ * is removed but it Votes remain standing.
+ * <li>protocol sends to all remainingparties a OptIn containing all received
+ * votes. Each party now must submit again a {@link Votes} object within
+ * {@link #PHASE_TIME} seconds. This new Votes must equal or extend the previous
+ * Votes of the party.
+ * <li>The protocol use the {@link MOPACState#getEvaluator()} to determine the
+ * ... . If there is such a subset, the parties that placed these votes reached
+ * an agreement. They are added to the agreement set, sent the {@link Finished}
+ * info and terminated. They are removed from the remainingparties.
+ * </ol>
+ * <li>Any remaining parties are sent a {@link Finished} object without
+ * agreement bid and are terminated.
+ * </ol>
+ * <p>
+ * A party can send {@link EndNegotiation} at any time to remove itself from the
+ * negotiation. The other parties may continue anyawy without him.
+ * <p>
+ * Parties receive the {@link Finished} information at the very end when all
+ * agreements have been collected.
+ * <p>
+ * This logs to "Protocol" logger if there are issues
+ * <p>
+ * This object is mutable: the internal state changes as parties interact with
+ * the protocol.
+ * <p>
+ * Threadsafe: all entrypoints are synhronized.
+ */
+public class MOPAC extends DefaultListenable<ProtocolEvent>
+		implements SessionProtocol {
+	/**
+	 * Tech notes. 0. Everything here is phase driven and runs due to callback
+	 * from parties. 1. Connect all parties. 2. If any party fails at any time,
+	 * add it to exceptions list in the state 3. Run timer to check the phase at
+	 * possible end time. The timer just checks the state, and therefore it does
+	 * no harm if it is double checked. 4. We run phases always with either
+	 * PHASE_TIME or time to the global deadline, whichever comes first.
+	 * Therefore phases naturally end at end of nego. 5. Phases must have at
+	 * least {@link #MINDURATION} otherwise it makes no sense to even start it.
+	 */
+	private static final int MINDURATION = 100;
+	private static final int MIN_SLEEP_TIME = 1000;
+	private static final int MAX_SLEEP_TIME = 60000;
+	private static final ProtocolRef MOPAC = new ProtocolRef("MOPAC");
+
+	private final Reporter log;
+	private MOPACState state = null; // mutable!
+
+	/**
+	 * Set to true after all is done: we sent final outcomes. This is needed
+	 * because we can't tell from state what we did at the very end.
+	 */
+	private boolean finished = false;
+	private Timer timer = null;
+
+	/**
+	 * 
+	 * @param state normally the initial state coming from SAOPSettings
+	 */
+	public MOPAC(MOPACState state, Reporter logger) {
+		if (state == null) {
+			throw new NullPointerException("state must be not null");
+		}
+		if (state.getSettings().getDeadline().getDuration() < MINDURATION) {
+			throw new IllegalArgumentException(
+					"Duration must be at least " + MINDURATION);
+		}
+		if (logger == null) {
+			throw new NullPointerException("Logger must be not null");
+		}
+		this.log = logger;
+		this.state = state;
+	}
+
+	@Override
+	public synchronized void start(
+			ProtocolToPartyConnFactory connectionfactory) {
+		try {
+			System.out.println("starting MOPAC");
+			// we're in Phase.INIT still
+			connect(connectionfactory);
+			setupParties();
+			long now = System.currentTimeMillis();
+			state = state.initPhase(now);
+			startPhase(now);
+		} catch (Throwable e) {
+			e.printStackTrace();
+			handleError("Failed to start MOPAC session", new PartyId("BUG"), e);
+		}
+	}
+
+	@Override
+	public String getDescription() {
+		return "All parties get YourTurn. They now can submit their Offer within 30 seconds. "
+				+ "Next they receive a Voting. They can now submit their Votes."
+				+ "Then they receive a a OptIn, now they can widen up their Votes."
+				+ "If one of their Vote succeeds, they finish with an agreement. "
+				+ "The VotingEvaluator setting determines the exact voting behaviour "
+				+ "and if this process repeats";
+	}
+
+	@Override
+	public void addParticipant(PartyWithProfile party)
+			throws IllegalStateException {
+		throw new IllegalStateException(
+				"Dynamic joining a negotiation is not supported in AMOP");
+	}
+
+	@Override
+	public MOPACState getState() {
+		return state;
+	}
+
+	@Override
+	public ProtocolRef getRef() {
+		return MOPAC;
+	}
+
+	/*******************************************************************
+	 * private functions. Some are protected only, for testing purposes
+	 ********************************************************************/
+	/**
+	 * step 1 in protocol: connect all involved parties and start the clock.
+	 * This always "succeeds" with a valid (but possibly final) state
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @param connectionfactory the connectionfactory for making party
+	 *                          connections
+	 * 
+	 * @throws InterruptedException if the connection procedure is unterrupted
+	 * 
+	 * @throws IOException          if this fails to properly conect to the
+	 *                              parties, eg interrupted or server not
+	 *                              responding..
+	 */
+	protected synchronized void connect(
+			ProtocolToPartyConnFactory connectionfactory)
+			throws InterruptedException, IOException {
+		List<PartyWithProfile> participants = state.getSettings()
+				.getAllParties();
+		List<Reference> parties = participants.stream()
+				.map(parti -> (parti.getParty().getPartyRef()))
+				.collect(Collectors.toList());
+		List<ProtocolToPartyConn> connections = null;
+		log.log(Level.INFO, "MOPAC connect " + parties);
+		while (connections == null) {
+			try {
+				connections = connectionfactory.connect(parties);
+			} catch (NoResourcesNowException e) {
+				long waitms = e.getLater().getTime()
+						- System.currentTimeMillis();
+				log.log(Level.INFO,
+						"No resources available to run session, waiting"
+								+ waitms);
+				Thread.sleep(Math.min(MAX_SLEEP_TIME,
+						Math.max(MIN_SLEEP_TIME, waitms)));
+			}
+		}
+		for (int i = 0; i < participants.size(); i++) {
+			state = state.with(connections.get(i), participants.get(i));
+		}
+	}
+
+	/**
+	 * step 2 in protocol: listen to connections and send settings to the
+	 * parties.
+	 * <p>
+	 * This is 'protected' to allow junit testing, this code is not a 'public'
+	 * part of the interface.
+	 * 
+	 * @throws ProtocolException if a party does not follow the protocol
+	 */
+	protected synchronized void setupParties() throws ProtocolException {
+		for (ProtocolToPartyConn conn : state.getConnections()) {
+			conn.addListener(action -> actionRequest(conn, action,
+					System.currentTimeMillis()));
+		}
+		Long now = System.currentTimeMillis();
+		state = state.with(
+				ProgressFactory.create(state.getSettings().getDeadline(), now));
+		for (ProtocolToPartyConn connection : state.getConnections()) {
+			try {
+				sendSettings(connection);
+			} catch (IOException e) {
+				state = state.with(new ProtocolException("Failed to initialize",
+						connection.getParty()));
+			}
+		}
+
+	}
+
+	/**
+	 * Send the {@link Inform} for the curreent phase to all parties and start
+	 * the time-out checker
+	 */
+	private void startPhase(long now) {
+		broadcastNegotiators(state.getPhase().getInform());
+		startTimer(1 + state.getPhase().getDeadline() - now);
+	}
+
+	/**
+	 * Check at given deadline if we already ended the phase.
+	 * 
+	 * @param deadn the deadline
+	 */
+	private void startTimer(long deadln) {
+		if (timer != null)
+			throw new IllegalStateException("Timer is still running!");
+		timer = new Timer("deadlinetimer");
+		timer.schedule(new TimerTask() {
+			@Override
+			public void run() {
+				checkEndPhase(System.currentTimeMillis());
+			}
+		}, deadln);
+
+	}
+
+	/**
+	 * Inform a party about its settings
+	 * 
+	 * @param connection
+	 * @throws IOException if party got disconnected
+	 */
+	private synchronized void sendSettings(ProtocolToPartyConn connection)
+			throws IOException {
+		PartyId partyid = connection.getParty();
+		ProfileRef profile = state.getPartyProfiles().get(partyid).getProfile();
+		Parameters params = state.getPartyProfiles().get(partyid).getParty()
+				.getParameters();
+		connection.send(new Settings(connection.getParty(), profile, getRef(),
+				state.getProgress(), params));
+	}
+
+	/**
+	 * This is called when one of the {@link ProtocolToPartyConn}s does an
+	 * action. Synchronized so that we always handle only 1 action at a time.
+	 * 
+	 * @param partyconn the connection on which the action came in
+	 * @param action    the {@link Action} taken by some party
+	 * @param now       current time
+	 */
+	protected synchronized void actionRequest(
+			final ProtocolToPartyConn partyconn, final Action action,
+			long now) {
+		System.out.println("received " + action);
+		state = state.with(partyconn.getParty(), action, now);
+		checkEndPhase(System.currentTimeMillis());
+	}
+
+	/**
+	 * The current phase may be completed. We check, because it may already been
+	 * handled. Proceed to next phase as needed. Reset the deadline timers and
+	 * inform parties. Increase progress if necessary. Must only be called
+	 * through {@link #endPhase} to ensure this is called only once.
+	 * 
+	 */
+	private synchronized void checkEndPhase(long now) {
+		if (!state.getPhase().isFinal(now))
+			return;
+		// phase indeed ended. Check what's up.
+		if (timer != null) {
+			timer.cancel();
+			timer = null;
+		}
+		state = state.finishPhase();
+		if (state.isFinal(now)) {
+			endNegotiation();
+			return;
+		}
+
+		state = state.nextPhase(now);
+		startPhase(now);
+	}
+
+	/**
+	 * Alternative to {@link ProtocolToPartyConnections#broadcast(Inform)},
+	 * sending only to currently negotiating parties
+	 * 
+	 * @param info the info to broadcast
+	 */
+	private void broadcastNegotiators(Inform info) {
+		System.out.println("broadcasting " + info);
+		for (PartyId party : state.getPhase().getPartyStates()
+				.getNegotiatingParties()) {
+			try {
+				state.getConnections().get(party).send(info);
+			} catch (IOException e) {
+				handleError("Party seems to have disconnected", party, e);
+			}
+		}
+	}
+
+	/**
+	 * Update state to include the given error and closes the party.
+	 * 
+	 * @param message The message to attach to the error
+	 * @param party   the party where the error occured
+	 * @param e       the exception that occured.
+	 */
+	private void handleError(final String message, final PartyId partyid,
+			final Throwable e) {
+		if (e instanceof ProtocolException) {
+			state = state.with((ProtocolException) e);
+		} else {
+			state = state.with(new ProtocolException(message, partyid, e));
+		}
+		log.log(Level.WARNING,
+				"MOPAC intercepted error from party " + partyid + ":" + message,
+				e);
+	}
+
+//	/**
+//	 * Sets the new state. If the new state is final, the finish-up procedure is
+//	 * executed. All state changes go through here to ensure we check isFinal
+//	 * before any state change.
+//	 * 
+//	 * @param newstate the new state.
+//	 */
+//	private synchronized void setState(MOPACState newstate) {
+//		long now = System.currentTimeMillis();
+//		if (state.isFinal(now)) {
+//			endNegotiation();
+//			return;
+//		}
+//
+//		this.state = newstate;
+//		if (newstate.isFinal(now)) {
+//			endNegotiation();
+//		}
+//	}
+
+	/**
+	 * To be called when we reach final state. Must only be called if
+	 * {@link MOPACState#isFinal(long)}. Send finished info to all parties.
+	 * Double calls are automatically ignored using the global finished flag.
+	 */
+	private synchronized void endNegotiation() {
+		if (finished)
+			return;
+		finished = true;
+		Finished info = new Finished(state.getAgreements());
+		for (ProtocolToPartyConn conn : state.getConnections()) {
+			try {
+				conn.send(info);
+				conn.close();
+			} catch (Exception e) {
+				log.log(Level.INFO, "Failed to send Finished to " + conn, e);
+			}
+		}
+		notifyListeners(new CurrentNegoState(state));
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACSettings.java	(revision 21)
@@ -0,0 +1,167 @@
+package geniusweb.protocol.session.mopac;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.deadline.Deadline;
+import geniusweb.protocol.session.OnePartyTeam;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.voting.VotingEvaluator;
+import tudelft.utilities.logging.Reporter;
+
+/**
+ * Settings for MOPAC negotiation. in MOPAC, each party may get a "power"
+ * parameter containing an natural number >=1.
+ */
+public class MOPACSettings implements SessionSettings {
+	private final List<OnePartyTeam> participants;
+	private final Deadline deadline;
+	private final VotingEvaluator votingevaluator;
+
+	/**
+	 * 
+	 * @param participants the list of {@link PartyWithProfile} in clockwise
+	 *                     order. There must be at least 2 to run the MOPAC
+	 *                     protocol. But SAOP can be initialized with less, for
+	 *                     use in TournamentSettings.
+	 * 
+	 *                     All provided participants must
+	 * 
+	 * @param deadline
+	 * @param minimumVotes the minimum number of votes allowed for a bid.
+	 */
+	@JsonCreator
+	public MOPACSettings(
+			@JsonProperty("participants") List<OnePartyTeam> participants,
+			@JsonProperty("deadline") Deadline deadline,
+			@JsonProperty("votingevaluator") VotingEvaluator votingeval) {
+		this.participants = participants;
+		this.deadline = deadline;
+		if (participants == null || deadline == null || votingeval == null)
+			throw new IllegalArgumentException(
+					"participants, deadline and votingeval must be not null");
+		this.votingevaluator = votingeval;
+		checkTeams();
+	}
+
+	@Override
+	public Double getMaxRunTime() {
+		return deadline.getDuration() / 1000d;
+	}
+
+	@Override
+	public MOPAC getProtocol(Reporter logger) {
+		return new MOPAC(new MOPACState(this), logger);
+	}
+
+	@Override
+	public List<TeamOfPartiesAndProfiles> getTeams() {
+		return Collections.unmodifiableList(participants);
+	}
+
+	/**
+	 * @return the deadline for this negotiation
+	 */
+	public Deadline getDeadline() {
+		return deadline;
+	}
+
+	@Override
+	public List<PartyWithProfile> getAllParties() {
+		return participants.stream()
+				.map(particip -> particip.getAllParties().get(0))
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * @return a class that allows us to evaluate the voting results in
+	 *         different ways, selectable by the user.
+	 */
+	public VotingEvaluator getVotingEvaluation() {
+		return votingevaluator;
+	}
+
+	@Override
+	public SessionSettings with(TeamOfPartiesAndProfiles party) {
+		if (!(party instanceof OnePartyTeam))
+			throw new IllegalArgumentException(
+					"Added party must be OnePartyTeam but got " + party);
+		List<OnePartyTeam> newparts = new LinkedList<>(participants);
+		newparts.add((OnePartyTeam) party);
+		return new MOPACSettings(newparts, deadline, votingevaluator);
+	}
+
+	@Override
+	public String toString() {
+		return "MOPACSettings[" + participants + "," + deadline + ","
+				+ votingevaluator.getClass().getSimpleName() + "]";
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((deadline == null) ? 0 : deadline.hashCode());
+		result = prime * result
+				+ ((participants == null) ? 0 : participants.hashCode());
+		result = prime * result
+				+ ((votingevaluator == null) ? 0 : votingevaluator.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		MOPACSettings other = (MOPACSettings) obj;
+		if (deadline == null) {
+			if (other.deadline != null)
+				return false;
+		} else if (!deadline.equals(other.deadline))
+			return false;
+		if (participants == null) {
+			if (other.participants != null)
+				return false;
+		} else if (!participants.equals(other.participants))
+			return false;
+		if (votingevaluator == null) {
+			if (other.votingevaluator != null)
+				return false;
+		} else if (!votingevaluator.equals(other.votingevaluator))
+			return false;
+		return true;
+	}
+
+	/**
+	 * @throws IllegalArgumentException if teams have improper power settings.
+	 */
+	private void checkTeams() {
+		for (OnePartyTeam party : participants) {
+			Object power = party.getParty().getParameters().get("power");
+			if (power != null) {
+				if (!(power instanceof Integer))
+					throw new IllegalArgumentException(
+							"parameter 'power' for party" + party
+									+ " must be integer but found " + power);
+				if ((Integer) power < 1)
+					throw new IllegalArgumentException(
+							"parameter 'power'  for party" + party
+									+ " must be >=1 but found " + power);
+
+			}
+		}
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACState.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/MOPACState.java	(revision 21)
@@ -0,0 +1,300 @@
+package geniusweb.protocol.session.mopac;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.SessionResult;
+import geniusweb.protocol.session.SessionState;
+import geniusweb.protocol.session.mopac.phase.OfferPhase;
+import geniusweb.protocol.session.mopac.phase.Phase;
+import geniusweb.protocol.session.saop.SAOPSettings;
+import geniusweb.references.PartyWithProfile;
+import geniusweb.voting.VotingEvaluator;
+
+public class MOPACState implements SessionState {
+
+	private final Phase phase;
+	private final MOPACSettings settings;
+	private final Map<PartyId, PartyWithProfile> partyprofiles;
+	private final ProtocolToPartyConnections connections;
+	private final List<Action> actions;
+	private final Progress progress;
+
+	/**
+	 * Creates the initial state from the given settings and progress=null
+	 * 
+	 * @param settings the {@link SAOPSettings}
+	 */
+	public MOPACState(MOPACSettings settings) {
+		this(null, Arrays.asList(),
+				new ProtocolToPartyConnections(Collections.emptyList()), null,
+				settings, Collections.emptyMap());
+
+	}
+
+	/**
+	 * @param phase         The Phase, or null if still initializing. Phase is
+	 *                      set something only after we have connections.
+	 * @param actions       the legal actions that have been done in the
+	 *                      negotiation. A new-sub-list is added for each phase
+	 *                      change. It is assumed that the last element of the
+	 *                      list is the list with actions taken in the current
+	 *                      phase. first action in first list is the oldest.
+	 *                      This MUST NOT contain illegal actions. Parties doing
+	 *                      illegal actions are killed and the offending action
+	 *                      ends up in the stacktrace. Previous actions of
+	 *                      crashed parties remain standing and valid.
+	 * @param conns         the existing party connections. we assume ownership
+	 *                      of this so it should not be modified although
+	 *                      connections may of course break.
+	 * @param progress      the {@link Progress} line. can be null if not yet
+	 *                      known. This happens because during setup we first
+	 *                      add all connections, and only when we have that we
+	 *                      set the correct deadline.
+	 * @param settings      the {@link SAOPSettings}
+	 * @param partyprofiles map with the {@link PartyWithProfile} for connected
+	 *                      parties. null is equivalent to an empty map.
+	 * @param evaluator     the {@link VotingEvaluator}
+	 */
+	@JsonCreator
+	protected MOPACState(@JsonProperty("phase") Phase phase,
+			@JsonProperty("actions") List<Action> actions,
+			@JsonProperty("connections") ProtocolToPartyConnections conns,
+			@JsonProperty("progress") Progress progress,
+			@JsonProperty("settings") MOPACSettings settings,
+			@JsonProperty("partyprofiles") Map<PartyId, PartyWithProfile> partyprofiles) {
+		this.phase = phase;
+		this.actions = actions;
+		this.connections = conns;
+		this.progress = progress;
+		this.settings = settings;
+		this.partyprofiles = partyprofiles;
+	}
+
+	/**
+	 * Can be called after all connections are initialized.
+	 * 
+	 * @param now current time ms since 1970
+	 * 
+	 * @return state with the initial partystates set.
+	 */
+	public MOPACState initPhase(long now) {
+		PartyStates partyStates = new PartyStates(getPowers());
+		Phase firstPhase = new OfferPhase(null, partyStates,
+				now + getAvailablePhaseTime(now),
+				settings.getVotingEvaluation());
+		return new MOPACState(firstPhase, actions, connections, progress,
+				settings, partyprofiles);
+	}
+
+	@Override
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	@Override
+	public Progress getProgress() {
+		return progress;
+	}
+
+	public Map<PartyId, PartyWithProfile> getPartyProfiles() {
+		return Collections.unmodifiableMap(partyprofiles);
+	}
+
+	/**
+	 * @return all current/remaining active party connections. Finished/crashed
+	 *         parties should be removed immediately.
+	 */
+	public ProtocolToPartyConnections getConnections() {
+		return connections;
+	}
+
+	/**
+	 * @param now current time ms since 1970
+	 * @return the max possible duration in ms of this phase. Maybe negative if
+	 *         ew are past deadline
+	 */
+	private Long getAvailablePhaseTime(long now) {
+		return Math.min(progress.getTerminationTime().getTime() - now,
+				Phase.PHASE_MAXTIME);
+	}
+
+	/**
+	 * 
+	 * @param connection   the new {@link ProtocolToPartyConn}
+	 * @param partyprofile the {@link PartyWithProfile} that is associated with
+	 *                     this state
+	 * @return new {@link MOPACState} with the new connection added. This call
+	 *         ignores the progress (does not check isFinal) because we uses
+	 *         this during the setup where the deadline is not yet relevant.
+	 */
+	protected MOPACState with(ProtocolToPartyConn connection,
+			PartyWithProfile partyprofile) {
+		if (phase != null)
+			throw new IllegalStateException(
+					"Adding connections only allowed while initializing");
+
+		ProtocolToPartyConnections newconns = getConnections().with(connection);
+		Map<PartyId, PartyWithProfile> newprofiles = new HashMap<>(
+				partyprofiles);
+		newprofiles.put(connection.getParty(), partyprofile);
+		return new MOPACState(null, actions, newconns, progress, settings,
+				newprofiles);
+	}
+
+	/**
+	 * @param id the {@link PartyId} of the party that failed.
+	 * @param e  the {@link ProtocolException} that occurred in the party
+	 * @return a new state with the error set.
+	 */
+	public MOPACState with(ProtocolException e) {
+		return new MOPACState(phase.with(e), actions, connections, progress,
+				settings, partyprofiles);
+	}
+
+	/**
+	 * Sets the progress for this session. Can be set only if progress=null.
+	 * Should be set in INIT phase.
+	 * 
+	 * @param newprogress the new progress
+	 * @return new {@link MOPACState} with the progress set
+	 */
+	public MOPACState with(Progress newprogress) {
+		if (progress != null || newprogress == null || phase != null) {
+			throw new IllegalArgumentException(
+					"progress must be null, newprogress must be not null and phase must be INIT");
+		}
+		return new MOPACState(phase, actions, connections, newprogress,
+				getSettings(), partyprofiles);
+	}
+
+	/**
+	 * Start the next phase. If new phase is OfferPhase, we increase progress.
+	 * Actions is reset to empty. does nothing if not
+	 * {@link #isNewPhasePossible(long)}
+	 * 
+	 * @param now current time
+	 * @return new {@link MOPACState} with phase initialized for next phase.
+	 */
+	public MOPACState nextPhase(long now) {
+		long remainingNegoTime = progress.getTerminationTime().getTime() - now;
+		Phase newphase = phase.next(now,
+				Math.min(remainingNegoTime, Phase.PHASE_MAXTIME));
+		Progress newprogress = progress;
+		if (newphase instanceof OfferPhase
+				&& progress instanceof ProgressRounds)
+			newprogress = ((ProgressRounds) progress).advance();
+
+		return new MOPACState(newphase, actions, connections, newprogress,
+				getSettings(), partyprofiles);
+	}
+
+	@Override
+	public boolean isFinal(long now) {
+		return phase != null && phase.isFinal(now) && !isNewPhasePossible(now);
+	}
+
+	/**
+	 * When this is called, all parties should have acted.
+	 * 
+	 * @param now
+	 * @return
+	 */
+	public boolean isNewPhasePossible(long now) {
+		return phase.getPartyStates().getNegotiatingParties().size() >= 2
+				&& getAvailablePhaseTime(now) > Phase.PHASE_MINTIME;
+	}
+
+	@Override
+	public Agreements getAgreements() {
+		return phase.getPartyStates().getAgreements();
+	}
+
+	@Override
+	public MOPACSettings getSettings() {
+		return settings;
+	}
+
+	public Phase getPhase() {
+		return phase;
+	}
+
+	/**
+	 * Check if action is allowed. Add action to the list of actions. Notice,
+	 * this does NOT check if we need to step to the next phase, because
+	 * deciding that is also depending on time-outs.
+	 * 
+	 * @param actor  the actor that did this action. Can be used to check if
+	 *               action is valid. NOTICE caller has to make sure the current
+	 *               state is not final. MUST NOT be null.
+	 * @param action the action that was proposed by actor. MUST NOT be null.
+	 * @param now    the current time in ms since 1970, see
+	 *               {@link System#currentTimeMillis()}
+	 */
+
+	public MOPACState with(PartyId actor, Action action, long now) {
+		return new MOPACState(phase.with(actor, action, now), actions,
+				connections, progress, settings, partyprofiles);
+	}
+
+	@Override
+	public SessionResult getResult() {
+		List<Double> emptylist = new LinkedList<>();
+		for (int n = 0; n < getConnections().size(); n++)
+			emptylist.add(0d);
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
+				emptylist, null);
+	}
+
+	/**
+	 * @return the power of all parties as set in their parameters, default =1.
+	 *         bad power values (non-integer, or <1) are ignored.
+	 */
+	private Map<PartyId, Integer> getPowers() {
+		Map<PartyId, Integer> map = new HashMap<>();
+		for (PartyId pid : partyprofiles.keySet()) {
+			Object power = partyprofiles.get(pid).getParty().getParameters()
+					.get("power");
+			if (power == null || !(power instanceof Integer)
+					|| ((Integer) power) < 1)
+				power = 1;
+			map.put(pid, (Integer) power);
+		}
+		return map;
+	}
+
+	@Override
+	public String toString() {
+		return "MOPACState[" + phase + "," + settings + "," + partyprofiles
+				+ "," + connections + "," + progress + "," + "]";
+	}
+
+	/**
+	 * 
+	 * @return a wrapped-up state, with all parties doen an action or kicked,
+	 *         and agreements collected
+	 */
+	public MOPACState finishPhase() {
+		Phase newphase = phase.finish();
+		LinkedList<Action> newactions = new LinkedList<Action>(actions);
+		newactions.addAll(newphase.getPartyStates().getActions());
+		return new MOPACState(newphase, newactions, connections, progress,
+				settings, partyprofiles);
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/PartyStates.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/PartyStates.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/PartyStates.java	(revision 21)
@@ -0,0 +1,235 @@
+package geniusweb.protocol.session.mopac;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.protocol.ProtocolException;
+
+/**
+ * The current state of all participating parties. A party either notYetActed,
+ * did action, reached an agreement, walked away or made a
+ * {@link ProtocolException}.
+ * <p>
+ * After a phase completes, the actions are filtered/handled to collect
+ * agreements and move remaining parties back to notYetActed.
+ * 
+ * This is not considering current time or checking actions.
+ */
+public class PartyStates {
+	// all parties must be in exactly one state at all times.
+	private final Set<PartyId> notYetActed;
+	private final List<Action> actions; // chrono order
+	private final Agreements agreements;
+	private final List<PartyId> walkedAway;
+	private final Map<PartyId, ProtocolException> exceptions;
+	private Map<PartyId, Integer> powers;
+
+	/**
+	 * Initial constructor.
+	 * 
+	 * @param powers the powers of all parties.
+	 */
+	public PartyStates(Map<PartyId, Integer> powers) {
+		this.notYetActed = new HashSet<>(powers.keySet());
+		this.actions = Collections.emptyList();
+		this.agreements = new Agreements();
+		this.walkedAway = Collections.emptyList();
+		this.exceptions = Collections.emptyMap();
+		this.powers = new HashMap<>(powers);
+	}
+
+	public PartyStates(Set<PartyId> notYetActed, List<Action> actions,
+			Agreements agreements, List<PartyId> walkedAway,
+			Map<PartyId, ProtocolException> exceptions,
+			Map<PartyId, Integer> powers) {
+		this.notYetActed = notYetActed;
+		this.actions = actions;
+		this.agreements = agreements;
+		this.walkedAway = walkedAway;
+		this.exceptions = exceptions;
+		this.powers = powers;
+	}
+
+	/**
+	 * 
+	 * @param action
+	 * @return new state with party done given action.
+	 * @throws IllegalArgumentException if party doing action already acted.
+	 *                                  This is just a safety check as legality
+	 *                                  of actions should be checked before
+	 *                                  calling this.
+	 */
+	public PartyStates with(Action action) {
+		if (!(notYetActed.contains(action.getActor())))
+			throw new IllegalArgumentException(
+					"actor already acted: " + action);
+		if (action instanceof EndNegotiation)
+			return withWalkAway(action.getActor());
+		List<Action> newActions = new LinkedList<Action>(actions);
+		newActions.add(action);
+		return new PartyStates(removeParty(action.getActor()), newActions,
+				agreements, walkedAway, exceptions, powers);
+	}
+
+	/**
+	 * 
+	 * @param newAgree a new agreements to be merged with existing agreements.
+	 *                 The parties in the agreement must have acted and thus in
+	 *                 actions.
+	 * @return new PartyStates with agreeing parties removed from actions and
+	 *         added to Agreements.
+	 */
+	public PartyStates with(Agreements newAgree) {
+		Set<PartyId> parties = newAgree.getMap().keySet();
+		List<Action> newActions = actions.stream()
+				.filter(act -> !(parties.contains(act.getActor())))
+				.collect(Collectors.toList());
+		return new PartyStates(notYetActed, newActions,
+				agreements.with(newAgree), walkedAway, exceptions, powers);
+	}
+
+	public PartyStates withWalkAway(PartyId actor) {
+		if (!(notYetActed.contains(actor)))
+			throw new IllegalArgumentException("actor already acted: " + actor);
+		LinkedList<PartyId> newWalkAway = new LinkedList<>(walkedAway);
+		newWalkAway.add(actor);
+		return new PartyStates(removeParty(actor), actions, agreements,
+				newWalkAway, exceptions, powers);
+	}
+
+	/**
+	 * Move party from active to exceptions.
+	 * 
+	 * @param e the exception that the party caused
+	 * @return new PartyStates with party that caused the exception in the
+	 *         exceptions list and removed from the active list. Nothing happens
+	 *         if party is not active.
+	 */
+	public PartyStates with(ProtocolException e) {
+		if (!notYetActed.contains(e.getParty())) {
+			// complex case. Party did valid action but now is messing around.
+			// Easiest seems to completely ignore it. CHECK.
+			return this;
+		}
+		Map<PartyId, ProtocolException> newExc = new HashMap<>(exceptions);
+		newExc.put(e.getParty(), e);
+		return new PartyStates(removeParty(e.getParty()), actions, agreements,
+				walkedAway, newExc, powers);
+	}
+
+	/**
+	 * 
+	 * @return parties that have not yet acted
+	 */
+	public Set<PartyId> getNotYetActed() {
+		return notYetActed;
+	}
+
+	/**
+	 * 
+	 * @return parties that are still in the negotiation. These are the parties
+	 *         that not yet acted plus the ones that did an action.
+	 */
+	public Set<PartyId> getNegotiatingParties() {
+		Set<PartyId> parties = actions.stream().map(act -> act.getActor())
+				.collect(Collectors.toSet());
+		parties.addAll(notYetActed);
+		return parties;
+	}
+
+	/**
+	 * 
+	 * @param actor
+	 * @return {@link #notYetActed} with actor removed
+	 */
+	private Set<PartyId> removeParty(PartyId actor) {
+		return removeParties(new HashSet<>(Arrays.asList(actor)));
+	}
+
+	public Agreements getAgreements() {
+		return agreements;
+	}
+
+	public Map<PartyId, Integer> getPowers() {
+		return Collections.unmodifiableMap(powers);
+	}
+
+	public Map<PartyId, ProtocolException> getExceptions() {
+		return Collections.unmodifiableMap(exceptions);
+	}
+
+	/**
+	 * 
+	 * @param parties the parties to remove
+	 * @return {@link #notYetActed} without the given parties.
+	 */
+	private Set<PartyId> removeParties(Collection<PartyId> parties) {
+		Set<PartyId> newActiveParties = new HashSet<>(notYetActed);
+		for (PartyId party : parties) {
+			if (!newActiveParties.remove(party))
+				throw new IllegalArgumentException(
+						"Party " + party + " is not active, can't be removed");
+		}
+		return newActiveParties;
+	}
+
+	/**
+	 * @return new state where all {@link #notYetActed} are moved to the
+	 *         exceptions list.
+	 */
+	public PartyStates finish() {
+		PartyStates newstate = this;
+		for (PartyId party : notYetActed) {
+			newstate = newstate
+					.with(new ProtocolException("Party did not act", party));
+		}
+		return newstate;
+	}
+
+	public List<Action> getActions() {
+		return Collections.unmodifiableList(actions);
+	}
+
+	/**
+	 * @param type the type of actions to extract
+	 * @return all actions of type done in this phase
+	 */
+	public <T extends Action> List<T> getActions(Class<T> type) {
+		return actions.stream().filter(act -> type.isInstance(act))
+				.map(act -> (T) act).collect(Collectors.toList());
+	}
+
+	@Override
+	public String toString() {
+		return "PartyStates[" + notYetActed + "," + actions + "," + agreements
+				+ "," + walkedAway + "," + exceptions + "]";
+	}
+
+	/**
+	 * 
+	 * @return states with all parties that are in {@link #actions} moved back
+	 *         to {@link #notYetActed}. This state is then ready for a next
+	 *         phase.
+	 * @throws IllegalStateException if {@link #notYetActed} is not empty
+	 */
+	public PartyStates flush() {
+		if (!notYetActed.isEmpty())
+			throw new IllegalStateException(
+					"Some parties did not yet act:" + notYetActed);
+		return new PartyStates(getNegotiatingParties(), Collections.emptyList(),
+				agreements, walkedAway, exceptions, powers);
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/phase/DefaultPhase.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/phase/DefaultPhase.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/phase/DefaultPhase.java	(revision 21)
@@ -0,0 +1,133 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+public abstract class DefaultPhase implements Phase {
+	protected final List<Class<? extends Action>> allowedActions;
+	protected final Long deadline; // unix timestamp ms since 1970
+	protected final Phase prevPhase;
+	protected final PartyStates partyStates;
+	protected final VotingEvaluator evaluator;
+
+	/**
+	 * 
+	 * @param actions
+	 * @param prevPhases
+	 * @param partyStates
+	 * @param allowed
+	 * @param deadline    deadline time ms since 1970
+	 */
+	public DefaultPhase(Phase prevPhase, PartyStates partyStates,
+			List<Class<? extends Action>> allowed, Long deadline,
+			VotingEvaluator evaluator) {
+		this.allowedActions = allowed;
+		this.partyStates = partyStates;
+		this.deadline = deadline;
+		this.prevPhase = prevPhase;
+		this.evaluator = evaluator;
+	}
+
+	@Override
+	public boolean isFinal(long now) {
+		return now >= deadline || partyStates.getNotYetActed().isEmpty();
+	}
+
+	@Override
+	public long getDeadline() {
+		return deadline;
+	}
+
+	/**
+	 * @return all votes done in this phase.
+	 */
+	public List<Votes> getVotes() {
+		return partyStates.getActions().stream()
+				.filter(act -> act instanceof Votes).map(act -> (Votes) act)
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * Check if actor can do given action. Basic checks:
+	 * <ul>
+	 * <li>real actor does not match action's actor
+	 * <li>deadline for this phase has passed
+	 * <li>action is not allowed in this phase
+	 * <li>actor already acted in this phase
+	 * </ul>
+	 * 
+	 * @param activeParties the currently active parties
+	 * @param actor
+	 * @param action
+	 * @param timems
+	 * @throws ProtocolException
+	 * @throw ProtocolException if the action violates the protocol, or null if
+	 *        ok
+	 */
+	protected void checkAction(PartyId actor, Action action, long timems)
+			throws ProtocolException {
+		if (action == null)
+			throw new ProtocolException("Action=null", actor);
+		if (!(actor.equals(action.getActor())))
+			throw new ProtocolException(
+					"Incorrect actor info in action:" + action.getActor(),
+					actor);
+		if (isFinal(timems))
+			throw new ProtocolException("passed deadline", actor);
+		if (!(allowedActions.contains(action.getClass())))
+			throw new ProtocolException(
+					"Action not allowed in " + this.getClass() + ":" + action,
+					actor);
+		if (!partyStates.getNotYetActed().contains(actor))
+			throw new ProtocolException("Actor can not act anymore", actor);
+		return;
+	}
+
+	/**
+	 * @param timems current time
+	 * @return current PartyStates
+	 */
+	@Override
+	public PartyStates getPartyStates() {
+		return partyStates;
+	}
+
+	@Override
+	public VotingEvaluator getEvaluator() {
+		return evaluator;
+	}
+
+	@Override
+	public Phase next(long now, long duration) {
+		if (duration < PHASE_MINTIME || duration > PHASE_MAXTIME)
+			throw new IllegalArgumentException("Bug, illegal duration");
+		if (!isFinal(now))
+			throw new IllegalStateException(
+					"next must only be called when phase isFinal");
+		if (!partyStates.getNotYetActed().isEmpty())
+			throw new IllegalStateException("Bug. State has not been finished");
+		return checkedNext(now + duration);
+	}
+
+	/**
+	 * 
+	 * @param deadline
+	 * @return the next phase
+	 */
+	protected abstract Phase checkedNext(long deadline);
+
+	@Override
+	public String toString() {
+		// don't print prev state, it will give HUGE list because it also
+		// contains prevstate.....
+		return getClass().getSimpleName() + "[" + partyStates + "," + deadline
+				+ "," + allowedActions + "," + evaluator + "]";
+	}
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OfferPhase.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OfferPhase.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OfferPhase.java	(revision 21)
@@ -0,0 +1,66 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.inform.YourTurn;
+import geniusweb.issuevalue.Bid;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+public class OfferPhase extends DefaultPhase {
+
+	public OfferPhase(Phase prevPhase, PartyStates partyStates, Long deadlinems,
+			VotingEvaluator evaluator) {
+		super(prevPhase, partyStates,
+				Arrays.asList(Offer.class, EndNegotiation.class), deadlinems,
+				evaluator);
+	}
+
+	@Override
+	public OfferPhase with(PartyId actor, Action action, long timems) {
+		try {
+			checkAction(actor, action, timems);
+		} catch (ProtocolException ex) {
+			return this.with(ex);
+		}
+		return new OfferPhase(prevPhase, partyStates.with(action), deadline,
+				evaluator);
+	}
+
+	@Override
+	public Inform getInform() {
+		return new YourTurn();
+	}
+
+	@Override
+	public OfferPhase with(ProtocolException e) {
+		System.out.println("Party kicked because of protocol exception:" + e);
+		return new OfferPhase(prevPhase, partyStates.with(e), deadline,
+				evaluator);
+	}
+
+	@Override
+	public Phase finish() {
+		return new OfferPhase(prevPhase, partyStates.finish(), deadline,
+				evaluator);
+	}
+
+	@Override
+	public VotingPhase checkedNext(long deadln) {
+		return new VotingPhase(this, partyStates.flush(), deadln, evaluator);
+	}
+
+	public List<Bid> getOffers() {
+		return partyStates.getActions(Offer.class).stream()
+				.map(offer -> offer.getBid()).collect(Collectors.toList());
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OptInPhase.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OptInPhase.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/phase/OptInPhase.java	(revision 21)
@@ -0,0 +1,96 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Agreements;
+import geniusweb.inform.Inform;
+import geniusweb.inform.OptIn;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.CollectedVotes;
+import geniusweb.voting.VotingEvaluator;
+
+public class OptInPhase extends DefaultPhase {
+
+	protected OptInPhase(Phase prevPhase, PartyStates partyStates,
+			Long deadlinems, VotingEvaluator evaluator) {
+		super(prevPhase, partyStates,
+				Arrays.asList(Votes.class, EndNegotiation.class), deadlinems,
+				evaluator);
+	}
+
+	@Override
+	public Inform getInform() {
+		return new OptIn(((VotingPhase) prevPhase).getVotes());
+	}
+
+	@Override
+	public Phase with(PartyId actor, Action action, long now) {
+		try {
+			checkAction(actor, action, now);
+			if (action instanceof Votes)
+				checkExtends(action);
+
+			return new OptInPhase(prevPhase, partyStates.with(action), deadline,
+					evaluator);
+
+		} catch (ProtocolException e) {
+			return this.with(e);
+		}
+	}
+
+	/**
+	 * Check that this action extends previous action.
+	 * 
+	 * @param action
+	 * @param phase
+	 * @throws ProtocolException if this action does not correctly extend
+	 *                           previous vote.
+	 */
+	private void checkExtends(Action action) throws ProtocolException {
+
+		Votes votes = (Votes) action;
+		PartyId actor = action.getActor();
+		// this actor is active so he must have voted in previous round.
+		Votes prevVotes = ((VotingPhase) prevPhase).getVotes().stream()
+				.filter(v -> v.getActor().equals(actor)).findFirst().get();
+		if (!(votes.isExtending(prevVotes)))
+			throw new ProtocolException("New votes " + action
+					+ " does not extend previous vote " + prevVotes, actor);
+	}
+
+	@Override
+	public OptInPhase with(ProtocolException e) {
+		return new OptInPhase(prevPhase, partyStates.with(e), deadline,
+				evaluator);
+	}
+
+	@Override
+	public OptInPhase finish() {
+		PartyStates states = partyStates.finish();
+		Map<PartyId, Votes> votesmap = states.getActions(Votes.class).stream()
+				.collect(Collectors.toMap(votes -> votes.getActor(), v -> v));
+		CollectedVotes allvotes = new CollectedVotes(votesmap,
+				states.getPowers());
+		Agreements newAgree = evaluator.create(allvotes).getAgreements();
+		if (!newAgree.getMap().isEmpty()) {
+			System.out.println("detected new agreements");
+		}
+		PartyStates finalStates = states.with(newAgree);
+
+		return new OptInPhase(prevPhase, finalStates, deadline, evaluator);
+	}
+
+	@Override
+	public OfferPhase checkedNext(long newdeadline) {
+		return new OfferPhase(this, partyStates.flush(), newdeadline,
+				evaluator);
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/phase/Phase.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/phase/Phase.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/phase/Phase.java	(revision 21)
@@ -0,0 +1,101 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Set;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+/**
+ * A Phase is a part of the round structure. In each round parties have to take
+ * multiple actions. Each action is part of a phase.
+ *
+ */
+public interface Phase {
+	public static final Long PHASE_MAXTIME = 30000l; // 30sec
+	public static final Long PHASE_MINTIME = 100l; // 100 millisec
+
+	/**
+	 * Handle an actor's action. If a {@link ProtocolException} occurs, this is
+	 * handled by updating the PartyStates
+	 * 
+	 * @param prevPhases the list of all previous phases
+	 * @param actor      the real actor
+	 * @param action     the action submitted by actor, which this phase can
+	 *                   really handle.
+	 * @param now        the current time
+	 * @return new VotingPhase
+	 */
+	public Phase with(PartyId actor, Action action, long now);
+
+	/**
+	 * 
+	 * @param e a {@link ProtocolException}
+	 * @return new Phase with a party disabled because it violated a protocol eg
+	 *         breaking a websocket link.
+	 */
+	public Phase with(ProtocolException e);
+
+	/**
+	 * 
+	 * @return the inform object to send to all parties at start of phase.
+	 */
+	public Inform getInform();
+
+	/**
+	 * 
+	 * @param now the current time
+	 * @return true iff past deadline or no more actions are allowed anyway.
+	 *         (which usually means, all parties have done exactly one act).
+	 *         Notice, this is the main reason that it is desirable to require
+	 *         all parties to act exactly once in each phase. Without such a
+	 *         rule, the protocol will always have to wait till the deadline.
+	 */
+	public boolean isFinal(long now);
+
+	/**
+	 * 
+	 * @return finished state. That means, all parties that did not act have
+	 *         been kicked, new agreements have been computed, etc.
+	 */
+	public Phase finish();
+
+	/**
+	 * Determines the next phase. Resets the actions field. Can only be called
+	 * after {@link #finish()}
+	 * 
+	 * @param now      the curren time
+	 * @param duration the max duration of the next phase. Must be between
+	 *                 {@link #PHASE_MINTIME} and {@link #PHASE_MAXTIME}. Also
+	 *                 make sure that now+duration is at most at the total
+	 *                 negotiation deadline.
+	 * @return the next phase, or null if negotiation is complete.
+	 */
+	public Phase next(long now, long duration);
+
+	/**
+	 * Get the voting evaluator
+	 * 
+	 * @return
+	 */
+	public VotingEvaluator getEvaluator();
+
+	/**
+	 * 
+	 * @return time (ms since 1970) at which phase ends. The phase may end
+	 *         before, but never after this.
+	 */
+	public long getDeadline();
+
+	/**
+	 * @param now the current time
+	 * 
+	 * @return the party states. Notice that someone must call
+	 *         {@link PartyStates#finish(Set)} if passed the deadline.
+	 */
+	public PartyStates getPartyStates();
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/mopac/phase/VotingPhase.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/mopac/phase/VotingPhase.java	(revision 21)
+++ protocol/src/main/java/geniusweb/protocol/session/mopac/phase/VotingPhase.java	(revision 21)
@@ -0,0 +1,58 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import java.util.Arrays;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Voting;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+public class VotingPhase extends DefaultPhase {
+
+	VotingPhase(Phase prevPhase, PartyStates partyStates, Long deadlinems,
+			VotingEvaluator evaluator) {
+		super(prevPhase, partyStates,
+				Arrays.asList(Votes.class, EndNegotiation.class), deadlinems,
+				evaluator);
+	}
+
+	@Override
+	public VotingPhase with(PartyId actor, Action action, long now) {
+		try {
+			checkAction(actor, action, now);
+		} catch (ProtocolException ex) {
+			return this.with(ex);
+		}
+		return new VotingPhase(prevPhase, partyStates.with(action), deadline,
+				evaluator);
+	}
+
+	@Override
+	public Inform getInform() {
+		return new Voting(((OfferPhase) prevPhase).getOffers(),
+				partyStates.getPowers());
+	}
+
+	@Override
+	public VotingPhase with(ProtocolException e) {
+		return new VotingPhase(prevPhase, partyStates.with(e), deadline,
+				evaluator);
+	}
+
+	@Override
+	public VotingPhase finish() {
+		return new VotingPhase(prevPhase, partyStates.finish(), deadline,
+				evaluator);
+	}
+
+	@Override
+	protected Phase checkedNext(long deadln) {
+		return new OptInPhase(this, partyStates.flush(), deadln, evaluator);
+	}
+
+}
Index: protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/saop/SAOP.java	(revision 21)
@@ -15,9 +15,9 @@
 import geniusweb.deadline.Deadline;
 import geniusweb.events.ProtocolEvent;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.progress.ProgressFactory;
 import geniusweb.protocol.CurrentNegoState;
@@ -227,5 +227,5 @@
 			} catch (IOException e) {
 				throw new ProtocolException("Failed to initialize",
-						connection.getParty().getName(), e);
+						connection.getParty(), e);
 			}
 		}
@@ -257,5 +257,5 @@
 	 * Synchronized so that we always handle only 1 action at a time.
 	 * 
-	 * @param partyconn the connection on which the action came in
+	 * @param partyconn the connection on which the action came in.
 	 * @param action    the {@link Action} taken by some party
 	 */
@@ -266,8 +266,8 @@
 			if (err == null) {
 				err = new ProtocolException("Party sent a null action",
-						partyconn.getParty().getName());
-			}
-			handleError(partyconn + "Protocol error",
-					partyconn.getParty().getName(), err);
+						partyconn.getParty());
+			}
+			handleError(partyconn + "Protocol error", partyconn.getParty(),
+					err);
 			return;
 		}
@@ -278,5 +278,5 @@
 				throw new ProtocolException(
 						"Party acts without having the turn",
-						partyconn.getParty().getName());
+						partyconn.getParty());
 			}
 			state.getConnections().broadcast(new ActionDone(action));
@@ -286,5 +286,5 @@
 		} catch (Throwable e) {
 			handleError("failed to handle action " + action,
-					partyconn.getParty().getName(), e);
+					partyconn.getParty(), e);
 		}
 
@@ -301,5 +301,5 @@
 			state.getConnections().get(party).send(new YourTurn());
 		} catch (IOException e) {
-			handleError("failed to send YourTurn", party.getName(), e);
+			handleError("failed to send YourTurn", party, e);
 		}
 	}
@@ -313,5 +313,5 @@
 	 */
 	private synchronized void handleError(final String message,
-			final String party, final Throwable e) {
+			final PartyId party, final Throwable e) {
 		if (e instanceof ProtocolException) {
 			setState(state.with((ProtocolException) e));
@@ -353,5 +353,5 @@
 		if (!isFinishedInfoSent.compareAndSet(false, true))
 			return;
-		Inform finished = new Finished(state.getAgreement());
+		Inform finished = new Finished(state.getAgreements());
 		for (ProtocolToPartyConn conn : state.getConnections()) {
 			sendFinish(conn, finished);
Index: protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/saop/SAOPSettings.java	(revision 21)
@@ -10,4 +10,5 @@
 
 import geniusweb.deadline.Deadline;
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionProtocol;
 import geniusweb.protocol.session.SessionSettings;
@@ -17,5 +18,5 @@
 
 public class SAOPSettings implements SessionSettings {
-	private final List<SaopPartyWithProfile> participants;
+	private final List<OnePartyTeam> participants;
 	private final Deadline deadline;
 
@@ -30,5 +31,5 @@
 	@JsonCreator
 	public SAOPSettings(
-			@JsonProperty("participants") List<SaopPartyWithProfile> participants,
+			@JsonProperty("participants") List<OnePartyTeam> participants,
 			@JsonProperty("deadline") Deadline deadline) {
 		this.participants = participants;
@@ -111,10 +112,9 @@
 	@Override
 	public SessionSettings with(TeamOfPartiesAndProfiles party) {
-		if (!(party instanceof SaopPartyWithProfile))
+		if (!(party instanceof OnePartyTeam))
 			throw new IllegalArgumentException(
-					"Added party must be SaopPartyWithProfile but got "
-							+ party);
-		List<SaopPartyWithProfile> newparts = new LinkedList<>(participants);
-		newparts.add((SaopPartyWithProfile) party);
+					"Added party must be OnePartyTeam but got " + party);
+		List<OnePartyTeam> newparts = new LinkedList<>(participants);
+		newparts.add((OnePartyTeam) party);
 		return new SAOPSettings(newparts, deadline);
 	}
Index: protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/saop/SAOPState.java	(revision 21)
@@ -6,4 +6,6 @@
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import geniusweb.actions.Accept;
@@ -12,4 +14,5 @@
 import geniusweb.actions.Offer;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
 import geniusweb.issuevalue.Bid;
 import geniusweb.progress.Progress;
@@ -113,13 +116,14 @@
 
 	@Override
-	public Bid getAgreement() {
+	public Agreements getAgreements() {
+		Agreements agree = new Agreements();
 		List<Action> acts = getActions();
 		int nparticipants = getConnections().size();
 		if (nparticipants < 2 || acts.size() < nparticipants) {
-			return null;
+			return agree;
 		}
 		Action offer = acts.get(acts.size() - nparticipants);
 		if (!(offer instanceof Offer))
-			return null;
+			return agree;
 		Bid bid = ((Offer) offer).getBid();
 
@@ -128,5 +132,16 @@
 				.allMatch(act -> act instanceof Accept
 						&& bid.equals(((Accept) act).getBid()));
-		return allaccept ? bid : null;
+		if (allaccept)
+			agree = agree.with(bid, getParties());
+		return agree;
+	}
+
+	/**
+	 * 
+	 * @return all currently connected parties.
+	 */
+	private Set<PartyId> getParties() {
+		return getConnections().stream().map(conn -> conn.getParty())
+				.collect(Collectors.toSet());
 	}
 
@@ -134,6 +149,6 @@
 	public boolean isFinal(long currentTimeMs) {
 		List<Action> acts = getActions();
-		return super.isFinal(currentTimeMs) || getAgreement() != null
-				|| (!acts.isEmpty()
+		return super.isFinal(currentTimeMs)
+				|| !getAgreements().getMap().isEmpty() || (!acts.isEmpty()
 						&& acts.get(acts.size() - 1) instanceof EndNegotiation);
 	}
@@ -254,5 +269,5 @@
 		for (int n = 0; n < getConnections().size(); n++)
 			emptylist.add(0d);
-		return new SessionResult(getSettings().getAllParties(), getAgreement(),
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
 				emptylist, getError());
 	}
Index: protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/shaop/BareSHAOPState.java	(revision 21)
@@ -158,5 +158,4 @@
 	}
 
-	@Override
 	public ProtocolException getError() {
 		return error;
@@ -182,5 +181,5 @@
 			penalties[partyNumbers.get(party)] = spent;
 		}
-		return new SessionResult(getSettings().getAllParties(), getAgreement(),
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
 				Arrays.asList(penalties), getError());
 
Index: protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOP.java	(revision 21)
@@ -21,9 +21,9 @@
 import geniusweb.deadline.Deadline;
 import geniusweb.events.ProtocolEvent;
-import geniusweb.party.inform.ActionDone;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
+import geniusweb.inform.ActionDone;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.progress.ProgressFactory;
 import geniusweb.protocol.CurrentNegoState;
@@ -238,5 +238,5 @@
 			} catch (IOException e) {
 				throw new ProtocolException("Failed to initialize",
-						connection.getParty().getName(), e);
+						connection.getParty(), e);
 			}
 		}
@@ -276,5 +276,5 @@
 		} catch (Throwable e) {
 			handleError("failed to handle action " + action,
-					partyconn.getParty().getName(), e);
+					partyconn.getParty(), e);
 		}
 	}
@@ -289,6 +289,5 @@
 
 		if (action == null)
-			throw new ProtocolException("Party sent a null action",
-					partyid.getName());
+			throw new ProtocolException("Party sent a null action", partyid);
 
 		// check if action allowed
@@ -296,12 +295,10 @@
 			if (!ALLOWED_ACTIONS.contains(action.getClass())) {
 				throw new ProtocolException(
-						"Illegal action for SHAOP Party:" + action,
-						partyid.getName());
+						"Illegal action for SHAOP Party:" + action, partyid);
 			}
 		} else {
 			if (!(action instanceof Comparison))
 				throw new ProtocolException(
-						"Illegal action for COB Party:" + action,
-						partyid.getName());
+						"Illegal action for COB Party:" + action, partyid);
 		}
 
@@ -316,6 +313,5 @@
 			if (!partyconn.getParty().equals(state.getCurrentTeam()))
 				throw new ProtocolException(
-						"Party acts without having the turn",
-						partyid.getName());
+						"Party acts without having the turn", partyid);
 			setState(state.with(partyconn.getParty(), action));
 			state.connections.broadcast(new ActionDone(action));
@@ -336,5 +332,5 @@
 			state.getConnections().get(party).send(new YourTurn());
 		} catch (IOException e) {
-			handleError("failed to send YourTurn", party.getName(), e);
+			handleError("failed to send YourTurn", party, e);
 		}
 	}
@@ -348,5 +344,5 @@
 	 */
 	private synchronized void handleError(final String message,
-			final String party, final Throwable e) {
+			final PartyId party, final Throwable e) {
 		log.log(Level.WARNING, "SHAOP protocol intercepted error due to party "
 				+ party + ": " + message, e);
@@ -388,5 +384,5 @@
 		if (!isFinishedInfoSent.compareAndSet(false, true))
 			return;
-		Inform finished = new Finished(state.getAgreement());
+		Inform finished = new Finished(state.getAgreements());
 		state.connections.stream().forEach(conn -> sendFinish(conn, finished));
 		notifyListeners(new CurrentNegoState(state));
Index: protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/session/shaop/SHAOPState.java	(revision 21)
@@ -6,4 +6,6 @@
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -17,4 +19,5 @@
 import geniusweb.actions.Offer;
 import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
 import geniusweb.issuevalue.Bid;
 import geniusweb.progress.Progress;
@@ -78,4 +81,13 @@
 	/**
 	 * 
+	 * @return the team leader {@link PartyId}s.
+	 */
+	private Set<PartyId> getLeaders() {
+		return partyNumbers.keySet().stream().filter(id -> isShaopParty(id))
+				.collect(Collectors.toSet());
+	}
+
+	/**
+	 * 
 	 * @param party a Party Id
 	 * @return the PartyId of the party (COB and SHAOP are partners)
@@ -88,6 +100,6 @@
 	public boolean isFinal(long currentTimeMs) {
 		List<Action> acts = getActions();
-		return super.isFinal(currentTimeMs) || getAgreement() != null
-				|| (!acts.isEmpty()
+		return super.isFinal(currentTimeMs)
+				|| !getAgreements().getMap().isEmpty() || (!acts.isEmpty()
 						&& acts.get(acts.size() - 1) instanceof EndNegotiation);
 	}
@@ -166,8 +178,9 @@
 
 	@Override
-	public Bid getAgreement() {
+	public Agreements getAgreements() {
+		Agreements agrees = new Agreements();
 		List<Action> acts = getActions();
 		if (acts.isEmpty())
-			return null;
+			return agrees;
 
 		int requiredaccepts = getSettings().getTeams().size() - 1;
@@ -179,10 +192,10 @@
 				continue;
 			if (requiredaccepts == 0 && act instanceof Offer)
-				return ((Offer) act).getBid();
+				return agrees.with(((Offer) act).getBid(), getLeaders());
 			if (!(act instanceof Accept))
-				return null;
+				return agrees;
 			requiredaccepts--;
 		}
-		return null;
+		return agrees;
 	}
 
Index: protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsSettings.java	(revision 21)
@@ -9,8 +9,10 @@
 import com.fasterxml.jackson.annotation.JsonProperty;
 
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
 import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
+import geniusweb.protocol.session.amop.AMOPSettings;
+import geniusweb.protocol.session.mopac.MOPACSettings;
 import geniusweb.protocol.session.saop.SAOPSettings;
-import geniusweb.protocol.session.saop.SaopPartyWithProfile;
 import geniusweb.protocol.session.shaop.SHAOPSettings;
 import geniusweb.protocol.session.shaop.ShaopTeam;
@@ -106,5 +108,7 @@
 			throw new IllegalArgumentException("nTournaments must be >0");
 		int teamsize;
-		if (sesettings instanceof SAOPSettings)
+		if (sesettings instanceof SAOPSettings
+				|| sesettings instanceof MOPACSettings
+				|| sesettings instanceof AMOPSettings)
 			teamsize = 1;
 		else if (sesettings instanceof SHAOPSettings)
@@ -283,6 +287,8 @@
 			public TeamOfPartiesAndProfiles apply(Team team,
 					ProfileList profilelist) {
-				if (sessionsettings instanceof SAOPSettings)
-					return new SaopPartyWithProfile(team.getParties().get(0),
+				if (sessionsettings instanceof SAOPSettings
+						|| sessionsettings instanceof AMOPSettings
+						|| sessionsettings instanceof MOPACSettings)
+					return new OnePartyTeam(team.getParties().get(0),
 							profilelist.getProfiles().get(0));
 				else if (sessionsettings instanceof SHAOPSettings) {
Index: protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java
===================================================================
--- protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java	(revision 20)
+++ protocol/src/main/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsState.java	(revision 21)
@@ -5,5 +5,4 @@
 import java.util.List;
 
-import geniusweb.protocol.ProtocolException;
 import geniusweb.protocol.session.SessionResult;
 import geniusweb.protocol.session.SessionSettings;
@@ -65,9 +64,4 @@
 	}
 
-	@Override
-	public ProtocolException getError() {
-		return null;
-	}
-
 	/**
 	 * 
Index: protocol/src/test/java/geniusweb/protocol/WillBeCalledTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/WillBeCalledTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/WillBeCalledTest.java	(revision 21)
@@ -0,0 +1,37 @@
+package geniusweb.protocol;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class WillBeCalledTest {
+	int counter;
+
+	@Test
+	public void testDeadline() throws InterruptedException {
+		counter = 0;
+		new WillBeCalled(() -> {
+			counter++;
+		}, 100l);
+		assertEquals(0, counter);
+		Thread.sleep(200);
+		assertEquals(1, counter);
+		Thread.sleep(200);
+		assertEquals(1, counter);
+
+	}
+
+	@Test
+	public void testManual() throws InterruptedException {
+		counter = 0;
+		WillBeCalled w = new WillBeCalled(() -> {
+			counter++;
+		}, 100l);
+		assertEquals(0, counter);
+		w.complete();
+		assertEquals(1, counter);
+		Thread.sleep(200);
+		assertEquals(1, counter);
+
+	}
+}
Index: protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/session/DefaultSessionStateTest.java	(revision 21)
@@ -23,5 +23,5 @@
 import geniusweb.actions.PartyId;
 import geniusweb.deadline.DeadlineTime;
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.progress.Progress;
 import geniusweb.progress.ProgressTime;
@@ -29,5 +29,4 @@
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
 import geniusweb.protocol.session.saop.SAOPSettings;
-import geniusweb.protocol.session.saop.SaopPartyWithProfile;
 import geniusweb.references.Parameters;
 import geniusweb.references.PartyRef;
@@ -60,10 +59,10 @@
 				new PartyRef("party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("profile2");
-		List<SaopPartyWithProfile> participants = Arrays.asList(
-				new SaopPartyWithProfile(party1ref, profile1),
-				new SaopPartyWithProfile(party2ref, profile2));
+		List<OnePartyTeam> participants = Arrays.asList(
+				new OnePartyTeam(party1ref, profile1),
+				new OnePartyTeam(party2ref, profile2));
 		SessionSettings settings = new SAOPSettings(participants, deadline);
 		ProtocolException e = new ProtocolException("test protocol error",
-				party1.getName());
+				party1);
 		HashMap<PartyId, PartyWithProfile> partyprofiles = new HashMap<PartyId, PartyWithProfile>();
 		partyprofiles.put(party1, new PartyWithProfile(party1ref, profile1));
@@ -135,9 +134,4 @@
 
 	@Override
-	public Bid getAgreement() {
-		return null;
-	}
-
-	@Override
 	public SessionResult getResult() {
 
@@ -145,8 +139,13 @@
 		for (int n = 0; n < getConnections().size(); n++)
 			emptylist.add(0d);
-		return new SessionResult(getSettings().getAllParties(), getAgreement(),
+		return new SessionResult(getSettings().getAllParties(), getAgreements(),
 				emptylist, getError());
 
 	}
 
+	@Override
+	public Agreements getAgreements() {
+		return new Agreements();
+	}
+
 }
Index: protocol/src/test/java/geniusweb/protocol/session/SessionResultTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/SessionResultTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/session/SessionResultTest.java	(revision 21)
@@ -7,4 +7,5 @@
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -16,4 +17,6 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
 import geniusweb.issuevalue.Bid;
 import geniusweb.issuevalue.DiscreteValue;
@@ -32,15 +35,17 @@
 	private final String ISSUE1 = "issue1";
 	private SessionResult result1, result1a, result2, result3, result4;
-	private String errorstring; // created dynamically: eclipse and maven
-								// generate different stacktrace.
-	private String jsonstring = "{\"participants\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile2\"}],\"agreement\":{\"issuevalues\":{\"issue1\":\"a\"}},\"penalties\":[0.0,0.0],\"error\":null}";
+	private String jsonstring = "{\"participants\":[{\"party\":{\"partyref\":\"party1\",\"parameters\":{}},\"profile\":\"profile1\"},{\"party\":{\"partyref\":\"party2\",\"parameters\":{}},\"profile\":\"profile2\"}],\"agreements\":{\"party2\":{\"issuevalues\":{\"issue1\":\"a\"}},\"party1\":{\"issuevalues\":{\"issue1\":\"a\"}}},\"penalties\":[0.0,0.0],\"error\":null}";
 	private List<Double> nopenalties = Arrays.asList(0d, 0d);
 	private List<Double> penalties = Arrays.asList(0.1d, 0.2d);
 
+	private PartyId PARTY1 = new PartyId("party1");
+	private PartyId PARTY2 = new PartyId("party2");
+	private PartyId PARTY3 = new PartyId("party3");
+
 	@Before
 	public void before() throws URISyntaxException, JsonProcessingException {
-		errorstring = "\"error\":{\"java.lang.RuntimeException\":"
+		String errorstring = "\"error\":{\"java.lang.RuntimeException\":"
 				+ jackson.writeValueAsString(error) + "}";
-		System.out.println(errorstring);
+		// System.out.println(errorstring);
 
 		PartyWithParameters party1 = new PartyWithParameters(
@@ -57,4 +62,6 @@
 		issuevalues1.put(ISSUE1, new DiscreteValue("a"));
 		Bid bid1 = new Bid(issuevalues1);
+		Agreements agreement1 = new Agreements().with(bid1,
+				new HashSet<>(Arrays.asList(PARTY1, PARTY2)));
 
 		// different order but that shouldn't matter
@@ -62,16 +69,18 @@
 		issuevalues2.put(ISSUE1, new DiscreteValue("b"));
 		Bid bid2 = new Bid(issuevalues2);
+		Agreements agreement2 = new Agreements().with(bid2,
+				new HashSet<>(Arrays.asList(PARTY1, PARTY3)));
 
 		result1 = new SessionResult(Arrays.asList(partyprofile1, partyprofile2),
-				bid1, nopenalties, null);
+				agreement1, nopenalties, null);
 		result1a = new SessionResult(
-				Arrays.asList(partyprofile1, partyprofile2), bid1, nopenalties,
-				null);
+				Arrays.asList(partyprofile1, partyprofile2), agreement1,
+				nopenalties, null);
 		result2 = new SessionResult(Arrays.asList(partyprofile1, partyprofile2),
-				bid2, nopenalties, null);
+				agreement2, nopenalties, null);
 		result3 = new SessionResult(Arrays.asList(partyprofile2, partyprofile1),
-				bid2, nopenalties, null);
+				agreement1, nopenalties, null);
 		result4 = new SessionResult(Arrays.asList(partyprofile2, partyprofile1),
-				bid2, penalties, null);
+				agreement1, penalties, null);
 
 		// IGNORE ERROR for now, it fails somewhere deep in maven suddenly.
@@ -90,8 +99,8 @@
 	public List<String> getGeneralTestStrings() {
 		return Arrays.asList(
-				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Bid.*issue1=\"a\".*0\\.0.*0\\.0.*null.*",
-				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Bid.*issue1=\"b\".*0\\.0.*0\\.0.*null.*",
-				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Bid.*issue1=\"b\".*0\\.0.*0\\.0.*null.*",
-				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Bid.*issue1=\"b\".*0\\.1.*0\\.2.*null.*"
+				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Agreements.*Bid.*issue1=\"a\".*0\\.0.*0\\.0.*null.*",
+				"SessionResult.*party1.*profile1.*,.*party2.*profile2.*Agreements.*Bid.*issue1=\"b\".*0\\.0.*0\\.0.*null.*",
+				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Agreements.*Bid.*issue1=\"a\".*0\\.0.*0\\.0.*null.*",
+				"SessionResult.*party2.*profile2.*,.*party1.*profile1.*Agreements.*Bid.*issue1=\"a\".*0\\.1.*0\\.2.*null.*"
 
 		);
Index: protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/session/amop/AMOPSettingsTest.java	(revision 21)
@@ -0,0 +1,149 @@
+package geniusweb.protocol.session.amop;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.deadline.Deadline;
+import geniusweb.deadline.DeadlineRounds;
+import geniusweb.deadline.DeadlineTime;
+import geniusweb.protocol.session.OnePartyTeam;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.ProfileRef;
+import tudelft.utilities.junit.GeneralTests;
+import tudelft.utilities.logging.ReportToLogger;
+
+public class AMOPSettingsTest extends GeneralTests<AMOPSettings> {
+
+	private OnePartyTeam partyprof1 = mock(OnePartyTeam.class);
+	private OnePartyTeam partyprof2 = mock(OnePartyTeam.class);
+	private OnePartyTeam partyprof3 = mock(OnePartyTeam.class);
+	private List<OnePartyTeam> participants2 = Arrays.asList(partyprof1,
+			partyprof2);
+	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
+			partyprof2, partyprof3);
+
+	private DeadlineTime deadline = mock(DeadlineTime.class);
+	private Deadline deadline2 = mock(Deadline.class);
+	private AMOPSettings settings1 = new AMOPSettings(participants2, deadline);
+	private AMOPSettings settings1a = new AMOPSettings(participants2, deadline);
+	private AMOPSettings settings2 = new AMOPSettings(participants3, deadline);
+	private AMOPSettings settings3 = new AMOPSettings(participants2, deadline2);
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private AMOPSettings sersettings;
+	private final String serialized = "{\"AMOPSettings\":{\"participants\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}}}}";
+	private List<OnePartyTeam> participants;
+
+	@Before
+	public void before() throws URISyntaxException {
+		when(deadline.getDuration()).thenReturn(1000l);
+		when(deadline.toString()).thenReturn("deadline");
+		when(deadline2.toString()).thenReturn("deadline2");
+
+		when(partyprof1.toString()).thenReturn("party and profile 1");
+		when(partyprof2.toString()).thenReturn("party and profile 2");
+		when(partyprof3.toString()).thenReturn("party and profile 3");
+
+		// SERIALIZABLE version with REAL objects. Still no workaround for
+		// this...
+		PartyWithParameters party1 = new PartyWithParameters(
+				new PartyRef("http://party1"), new Parameters());
+		ProfileRef profile1 = new ProfileRef("http://profile1");
+		PartyWithParameters party2 = new PartyWithParameters(
+				new PartyRef("http://party2"), new Parameters());
+		ProfileRef profile2 = new ProfileRef("http://profile2");
+		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
+		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
+		participants = Arrays.asList(partywithprof1, partywithprof2);
+
+		Deadline deadlinetime = new DeadlineTime(100);
+		sersettings = new AMOPSettings(participants, deadlinetime);
+
+	}
+
+	@Override
+	public List<List<AMOPSettings>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(settings1, settings1a),
+				Arrays.asList(settings2), Arrays.asList(settings3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"AMOPSettings.party and profile 1, party and profile 2.,deadline.",
+				"AMOPSettings.party and profile 1, party and profile 2, party and profile 3.,deadline.",
+				"AMOPSettings.party and profile 1, party and profile 2.,deadline2.");
+	}
+
+	@Test
+	public void getProtocolTest() {
+		assertEquals("AMOP", settings1.getProtocol(new ReportToLogger("test"))
+				.getRef().getURI().toString());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void constructorNoDeadlineTest() {
+		new AMOPSettings(participants2, null);
+	}
+
+	@Test
+	public void testMaxRuntime() {
+		when(deadline.getDuration()).thenReturn(234l);
+		assertEquals((Double) .234d, settings1.getMaxRunTime());
+	}
+
+	@Test
+	public void testMaxRuntimeRounds() {
+		deadline = mock(DeadlineRounds.class);
+		when(deadline.getDuration()).thenReturn(12000l);
+
+		AMOPSettings settings = new AMOPSettings(participants2, deadline);
+
+		assertEquals((Double) 12d, settings.getMaxRunTime());
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		SessionSettings obj = jackson.readValue(serialized,
+				SessionSettings.class);
+		System.out.println(obj);
+		assertEquals(sersettings, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(sersettings);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+
+	@Test
+	public void testWith() {
+		AMOPSettings saop = new AMOPSettings(participants2, deadline);
+		SessionSettings saop2 = saop.with(partyprof3);
+		assertEquals(3, saop2.getTeams().size());
+	}
+
+	@Test
+	public void testGetAllParties() {
+		assertEquals(participants, sersettings.getAllParties());
+	}
+
+}
Index: protocol/src/test/java/geniusweb/protocol/session/amop/AMOPStateTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/amop/AMOPStateTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/session/amop/AMOPStateTest.java	(revision 21)
@@ -0,0 +1,436 @@
+package geniusweb.protocol.session.amop;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import geniusweb.actions.Accept;
+import geniusweb.actions.Action;
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Vote;
+import geniusweb.actions.Votes;
+import geniusweb.inform.Agreements;
+import geniusweb.issuevalue.Bid;
+import geniusweb.progress.Progress;
+import geniusweb.progress.ProgressRounds;
+import geniusweb.progress.ProgressTime;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.amop.AMOPState.Phase;
+import geniusweb.references.PartyWithProfile;
+import tudelft.utilities.junit.GeneralTests;
+
+public class AMOPStateTest extends GeneralTests<AMOPState> {
+	private final long NOW = 1000;
+	Bid a = mock(Bid.class), b = mock(Bid.class), c = mock(Bid.class),
+			d = mock(Bid.class);
+
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+	private final PartyId party4 = new PartyId("party4");
+	private final List<Action> actions = new LinkedList<>();
+	private final Action action = mock(Action.class);
+	private final List<Action> actions2 = Arrays.asList(action);
+
+	private final ProtocolToPartyConn party1conn = mock(
+			ProtocolToPartyConn.class);
+	private final ProtocolToPartyConn party2conn = mock(
+			ProtocolToPartyConn.class);
+	private final ProtocolToPartyConn party3conn = mock(
+			ProtocolToPartyConn.class);
+
+	private final ProtocolToPartyConnections connections = new ProtocolToPartyConnections(
+			Arrays.asList(party1conn, party2conn));
+	private final ProtocolToPartyConnections connections2 = new ProtocolToPartyConnections(
+			Arrays.asList(party1conn, party3conn));
+	private final ProtocolToPartyConnections connections3 = new ProtocolToPartyConnections(
+			Arrays.asList(party1conn, party2conn, party3conn));
+
+	private final Progress progresstime = mock(ProgressTime.class);
+	private final ProgressRounds progressrounds = mock(ProgressRounds.class);
+	private final ProgressRounds progressrounds1 = mock(ProgressRounds.class);
+	private final AMOPSettings settings = mock(AMOPSettings.class);
+	private final AMOPSettings settings2 = mock(AMOPSettings.class);
+	private AMOPState state1, state1a, state2, state3, state4, state5, state6,
+			state7, state8, state9;
+
+	private final Bid bid1 = mock(Bid.class);
+	private final Bid otherbid = mock(Bid.class);
+	private final Accept accept1 = new Accept(party1, bid1);
+	private final Accept accept2 = new Accept(party2, bid1);
+	private final Accept acceptother = new Accept(party2, otherbid);
+	private final Offer offer1 = new Offer(party1, bid1);
+	private final EndNegotiation endNegotiation1 = new EndNegotiation(party1);
+
+	private final Map<PartyId, PartyWithProfile> partyprofiles1 = mock(
+			HashMap.class);
+	private final Map<PartyId, PartyWithProfile> partyprofiles2 = mock(
+			HashMap.class);
+
+	private final Agreements agreements1 = new Agreements();
+	private final Agreements agreements2 = mock(Agreements.class);
+
+	private final Map<PartyId, ProtocolException> exceptions1 = mock(
+			HashMap.class);
+	private final Map<PartyId, ProtocolException> exceptions2 = mock(
+			HashMap.class);
+
+	private final List<PartyId> walkedaway0 = Arrays.asList();
+	private final List<PartyId> walkedaway1 = Arrays.asList(party1);
+	private final List<PartyId> walkedaway3 = Arrays.asList(party3);
+
+	@Before
+	public void before() {
+		when(party1conn.getParty()).thenReturn(party1);
+		when(party2conn.getParty()).thenReturn(party2);
+		when(party3conn.getParty()).thenReturn(party3);
+		when(party1conn.toString()).thenReturn("conn1");
+		when(party2conn.toString()).thenReturn("conn2");
+		when(party3conn.toString()).thenReturn("conn3");
+		when(action.toString()).thenReturn("act1");
+
+		when(progressrounds.getTerminationTime())
+				.thenReturn(new Date(System.currentTimeMillis() + 3600000l));
+		when(progressrounds1.getTerminationTime())
+				.thenReturn(new Date(System.currentTimeMillis() + 3600000l));
+		when(progressrounds.advance()).thenReturn(progressrounds1);
+
+		state1 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state1a = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state2 = new AMOPState(Phase.INIT, actions2, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state3 = new AMOPState(Phase.INIT, actions, connections2, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state4 = new AMOPState(Phase.INIT, actions, connections, progressrounds,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state5 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings2, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state6 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles2, agreements1, exceptions1,
+				walkedaway0);
+		state7 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements2, exceptions1,
+				walkedaway0);
+		state8 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions2,
+				walkedaway0);
+		state9 = new AMOPState(Phase.INIT, actions, connections, progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway3);
+	}
+
+	@Override
+	public List<List<AMOPState>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(state1, state1a),
+				Arrays.asList(state2), Arrays.asList(state3),
+				Arrays.asList(state4), Arrays.asList(state5),
+				Arrays.asList(state6), Arrays.asList(state7),
+				Arrays.asList(state8), Arrays.asList(state9));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		// being lazy, toString is terrible large and everything's mocked
+		return Arrays.asList("AMOPState.*", "AMOPState.*", "AMOPState.*",
+				"AMOPState.*", "AMOPState.*", "AMOPState.*", "AMOPState.*",
+				"AMOPState.*", "AMOPState.*");
+	}
+
+	@Test
+	public void constructWith0Connection() {
+		state1 = new AMOPState(Phase.INIT, actions,
+				new ProtocolToPartyConnections(Arrays.asList()), progresstime,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+	}
+
+	@Test
+	public void constructWithNullConnection() {
+		// is allowed
+		new AMOPState(Phase.INIT, actions, null, progresstime, settings,
+				partyprofiles1, agreements1, exceptions1, walkedaway0);
+	}
+
+	@Test
+	public void isInitFinalTest() {
+		assertFalse(state1.isFinal(NOW));
+	}
+
+	@Test
+	public void isInitFinalTest2() {
+		assertFalse(state1.isFinal(NOW + 12000));
+	}
+
+	@Test
+	public void withExceptionTest() {
+		AMOPState newstate1 = state1.with(party1,
+				mock(ProtocolException.class));
+		// party errors are not terminating in AMOP
+		assertFalse(newstate1.isFinal(NOW));
+		AMOPState newstate2 = newstate1.with(party2,
+				mock(ProtocolException.class));
+		assertFalse(newstate2.isFinal(NOW));
+	}
+
+	@Test
+	public void isOfferAgreementTest1() {
+		List<Action> actions = Arrays.asList(offer1);
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		assertTrue(state.getAgreements().getMap().isEmpty());
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void illegalActionInIllegalState() throws ProtocolException {
+		// party can not do accept. And certainly not in INIT phase.
+		state1.with(party1, offer1).with(party1, accept2);
+	}
+
+	@Test
+	public void isOfferAllowedInOFFER() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void isOfferAllowedInINIT() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void isOfferAllowedInVOTE() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void isOfferAcceptAgreementTest2() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, offer1).with(party1, accept2);
+	}
+
+	@Test
+	public void testWithProgress() {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections, null,
+				settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		state = state.with(progresstime);
+		assertEquals(progresstime, state.getProgress());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void testWithProgressAlreadySet() {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(progresstime);
+	}
+
+	@Test
+	public void activePartiesWalkedAwayTest() {
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway3);
+		assertEquals(Arrays.asList(party1, party2), state.getActiveParties());
+	}
+
+	@Test
+	public void activePartiesAgreedTest() {
+		Agreements agreements = new Agreements().with(bid1,
+				new HashSet<>(Arrays.asList(party1, party2)));
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progressrounds, settings, partyprofiles1, agreements,
+				exceptions1, walkedaway0);
+		assertEquals(Arrays.asList(party3), state.getActiveParties());
+	}
+
+	@Test
+	public void activePartiesErrorTest() {
+		Map<PartyId, ProtocolException> exceptions = Collections
+				.singletonMap(party2, new ProtocolException("test", party2));
+		AMOPState state = new AMOPState(Phase.INIT, actions, connections3,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions, walkedaway0);
+		assertEquals(Arrays.asList(party1, party3), state.getActiveParties());
+	}
+
+	/************** isFinal tests *****************/
+	@Test
+	public void deadlineProgressTimeDeadlineTest() {
+		Progress progress = mock(Progress.class);
+		when(progress.isPastDeadline(any())).thenReturn(true);
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progress, settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway0);
+		assertTrue(state.isFinal(10));
+	}
+
+	@Test
+	public void deadlineProgressTestOnly1Active() {
+		Progress progress = mock(Progress.class);
+		when(progress.isPastDeadline(any())).thenReturn(false);
+		List<PartyId> walkedaway13 = Arrays.asList(party1, party3);
+
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progress, settings, partyprofiles1, agreements1, exceptions1,
+				walkedaway13);
+		assertTrue(state.isFinal(10));
+	}
+
+	/*************
+	 * action tests
+	 ***************/
+	@Test
+	public void OfferTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new Offer(party1, bid1));
+		assertEquals(1, state.getActions().size());
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void OfferTwiceTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new Offer(party1, bid1));
+		// party1 may offer only once
+		state = state.with(party1, new Offer(party1, bid1));
+	}
+
+	@Test
+	public void endNegoOfferTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new EndNegotiation(party1));
+		assertEquals(1, state.getActions().size());
+		assertEquals(1, state.getWalkedAway().size());
+	}
+
+	@Test
+	public void endNegoVoteTest() throws ProtocolException {
+		AMOPState state = new AMOPState(Phase.VOTE, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway0);
+		state = state.with(party1, new EndNegotiation(party1));
+		assertEquals(1, state.getActions().size());
+		assertEquals(1, state.getWalkedAway().size());
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void OfferButInactive() throws ProtocolException {
+		// party walked away or error'ed and then votes again
+		AMOPState state = new AMOPState(Phase.OFFER, actions, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway1);
+		state = state.with(party1, new Offer(party1, bid1));
+	}
+
+	/************** NextPhase tests *****************/
+	@Test
+	public void isInitToOfferWorking() throws ProtocolException {
+		AMOPState state = state1.nextPhase();
+		assertEquals(Phase.OFFER, state.getPhase());
+	}
+
+	@Test
+	public void testCollectVotes() {
+		Votes vote1AB = new Votes(party1,
+				Arrays.asList(new Vote(party1, a, 2), new Vote(party1, b, 2)));
+		Votes vote2AB = new Votes(party2,
+				Arrays.asList(new Vote(party2, a, 2), new Vote(party2, b, 2)));
+		Votes vote3C = new Votes(party3, Arrays.asList(new Vote(party3, c, 2)));
+		Votes vote4AC = new Votes(party4,
+				Arrays.asList(new Vote(party4, a, 2), new Vote(party4, c, 2)));
+		// party 1,2,4 vote for A, party 1,2 vote for B, party 3,4 vote for C.
+		// the biggest vote is P,Q,S
+		Map<PartyId, Votes> votes = new HashMap<>();
+		votes.put(party1, vote1AB);
+		votes.put(party2, vote2AB);
+		votes.put(party3, vote3C);
+		votes.put(party4, vote4AC);
+
+		List<Action> actions1 = Arrays.asList(vote1AB, vote2AB, vote3C,
+				vote4AC);
+		AMOPState state = new AMOPState(Phase.VOTE, actions1, connections,
+				progresstime, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway1);
+		Agreements agrees = state.collectVotes(votes);
+		System.out.println(agrees);
+
+		// biggest agreement is party 1,2,4 for bid a.
+		assertEquals(new HashSet<>(Arrays.asList(party1, party2, party4)),
+				agrees.getMap().keySet());
+
+	}
+
+	@Test
+	public void testNextPhase() {
+		// copy of above. todo cleanup
+		Votes vote1AB = new Votes(party1,
+				Arrays.asList(new Vote(party1, a, 2), new Vote(party1, b, 2)));
+		Votes vote2AB = new Votes(party2,
+				Arrays.asList(new Vote(party2, a, 2), new Vote(party2, b, 2)));
+		Votes vote3C = new Votes(party3, Arrays.asList(new Vote(party3, c, 2)));
+		Votes vote4AC = new Votes(party4,
+				Arrays.asList(new Vote(party4, a, 2), new Vote(party4, c, 2)));
+		// party 1,2,4 vote for A, party 1,2 vote for B, party 3,4 vote for C.
+		// the biggest vote is P,Q,S
+		Map<PartyId, Votes> votes = new HashMap<>();
+		votes.put(party1, vote1AB);
+		votes.put(party2, vote2AB);
+		votes.put(party3, vote3C);
+		votes.put(party4, vote4AC);
+
+		List<Action> actions1 = Arrays.asList(vote1AB, vote2AB, vote3C,
+				vote4AC);
+		AMOPState state = new AMOPState(Phase.VOTE, actions1, connections,
+				progressrounds, settings, partyprofiles1, agreements1,
+				exceptions1, walkedaway1);
+		state = state.nextPhase();
+		assertEquals(Phase.OFFER, state.getPhase());
+		assertEquals(new HashSet<>(Arrays.asList(party1, party2, party4)),
+				state.getAgreements().getMap().keySet());
+		assertEquals(progressrounds.advance(), state.getProgress());
+
+	}
+}
Index: protocol/src/test/java/geniusweb/protocol/session/amop/AMOPTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/amop/AMOPTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/session/amop/AMOPTest.java	(revision 21)
@@ -0,0 +1,6 @@
+package geniusweb.protocol.session.amop;
+
+public class AMOPTest {
+
+	// TODO did not have time for this.
+}
Index: protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACSettingsTest.java	(revision 21)
@@ -0,0 +1,165 @@
+package geniusweb.protocol.session.mopac;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.deadline.Deadline;
+import geniusweb.deadline.DeadlineRounds;
+import geniusweb.deadline.DeadlineTime;
+import geniusweb.protocol.session.OnePartyTeam;
+import geniusweb.protocol.session.SessionSettings;
+import geniusweb.references.Parameters;
+import geniusweb.references.PartyRef;
+import geniusweb.references.PartyWithParameters;
+import geniusweb.references.ProfileRef;
+import geniusweb.voting.VotingEvaluator;
+import geniusweb.voting.votingevaluators.LargestAgreement;
+import tudelft.utilities.junit.GeneralTests;
+import tudelft.utilities.logging.ReportToLogger;
+
+public class MOPACSettingsTest extends GeneralTests<MOPACSettings> {
+
+	private final ObjectMapper jackson = new ObjectMapper();
+	private final Parameters noparams = new Parameters();
+	private OnePartyTeam partyprof1 = mockParty("party1", noparams);
+	private OnePartyTeam partyprof2 = mockParty("party2", noparams);
+	private OnePartyTeam partyprof3 = mockParty("party3", noparams);
+	private List<OnePartyTeam> participants2 = Arrays.asList(partyprof1,
+			partyprof2);
+	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
+			partyprof2, partyprof3);
+
+	private final VotingEvaluator ve = new LargestAgreement();
+
+	private DeadlineTime deadline = mock(DeadlineTime.class);
+	private Deadline deadline2 = mock(Deadline.class);
+	private MOPACSettings settings1 = new MOPACSettings(participants2, deadline,
+			ve);
+	private MOPACSettings settings1a = new MOPACSettings(participants2,
+			deadline, ve);
+	private MOPACSettings settings2 = new MOPACSettings(participants3, deadline,
+			ve);
+	private MOPACSettings settings3 = new MOPACSettings(participants2,
+			deadline2, ve);
+
+	private MOPACSettings sersettings;
+	private final String serialized = "{\"MOPACSettings\":{\"participants\":[{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"}],\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
+	private List<OnePartyTeam> participants;
+
+	private OnePartyTeam mockParty(String partyname, Parameters params) {
+		OnePartyTeam team = mock(OnePartyTeam.class);
+		PartyWithParameters pwithp = mock(PartyWithParameters.class);
+		when(team.getParty()).thenReturn(pwithp);
+		when(team.toString()).thenReturn(partyname);
+		when(pwithp.toString()).thenReturn(partyname);
+		when(pwithp.getParameters()).thenReturn(params);
+		return team;
+	}
+
+	@Before
+	public void before() throws URISyntaxException {
+
+		when(deadline.getDuration()).thenReturn(1000l);
+		when(deadline.toString()).thenReturn("deadline");
+		when(deadline2.toString()).thenReturn("deadline2");
+
+		// SERIALIZABLE version with REAL objects. Still no workaround for
+		// this...
+		PartyWithParameters party1 = new PartyWithParameters(
+				new PartyRef("http://party1"), new Parameters());
+		ProfileRef profile1 = new ProfileRef("http://profile1");
+		PartyWithParameters party2 = new PartyWithParameters(
+				new PartyRef("http://party2"), new Parameters());
+		ProfileRef profile2 = new ProfileRef("http://profile2");
+		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
+		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
+		participants = Arrays.asList(partywithprof1, partywithprof2);
+
+		Deadline deadlinetime = new DeadlineTime(100);
+		sersettings = new MOPACSettings(participants, deadlinetime, ve);
+
+	}
+
+	@Override
+	public List<List<MOPACSettings>> getGeneralTestData() {
+		return Arrays.asList(Arrays.asList(settings1, settings1a),
+				Arrays.asList(settings2), Arrays.asList(settings3));
+	}
+
+	@Override
+	public List<String> getGeneralTestStrings() {
+		return Arrays.asList(
+				"MOPACSettings.*party1.*party2.*deadline.*LargestAgreement.*",
+				"MOPACSettings.*party1.*party2.*deadline.*LargestAgreement.*",
+				"MOPACSettings.*party1.*party2.*deadline2.*LargestAgreement.*");
+	}
+
+	@Test
+	public void getProtocolTest() {
+		assertEquals("MOPAC", settings1.getProtocol(new ReportToLogger("test"))
+				.getRef().getURI().toString());
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void constructorNoDeadlineTest() {
+		new MOPACSettings(participants2, null, ve);
+	}
+
+	@Test
+	public void testMaxRuntime() {
+		when(deadline.getDuration()).thenReturn(234l);
+		assertEquals((Double) .234d, settings1.getMaxRunTime());
+	}
+
+	@Test
+	public void testMaxRuntimeRounds() {
+		deadline = mock(DeadlineRounds.class);
+		when(deadline.getDuration()).thenReturn(12000l);
+
+		MOPACSettings settings = new MOPACSettings(participants2, deadline, ve);
+
+		assertEquals((Double) 12d, settings.getMaxRunTime());
+	}
+
+	@Test
+	public void testDeserialize() throws IOException {
+		SessionSettings obj = jackson.readValue(serialized,
+				SessionSettings.class);
+		System.out.println(obj);
+		assertEquals(sersettings, obj);
+	}
+
+	@Test
+	public void testSerialize()
+			throws JsonProcessingException, URISyntaxException {
+
+		String string = jackson.writeValueAsString(sersettings);
+		System.out.println(string);
+		assertEquals(serialized, string);
+	}
+
+	@Test
+	public void testWith() {
+		MOPACSettings saop = new MOPACSettings(participants2, deadline, ve);
+		SessionSettings saop2 = saop.with(partyprof3);
+		assertEquals(3, saop2.getTeams().size());
+	}
+
+	@Test
+	public void testGetAllParties() {
+		assertEquals(participants, sersettings.getAllParties());
+	}
+
+}
Index: protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/session/mopac/MOPACTest.java	(revision 21)
@@ -0,0 +1,140 @@
+package geniusweb.protocol.session.mopac;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import geniusweb.actions.Action;
+import geniusweb.actions.Offer;
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Inform;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConn;
+import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
+import tudelft.utilities.logging.Reporter;
+import tudelft.utilities.repository.NoResourcesNowException;
+
+public class MOPACTest {
+
+	private final ObjectMapper jackson = new ObjectMapper();
+
+	private static final PartyId PARTY1ID = new PartyId("party1");
+	private static final PartyId PARTY2ID = new PartyId("party2");
+	private static final PartyId PARTY3ID = new PartyId("party3");
+
+	// don't try to mock settings, all parts are needed and mocking
+	// makes the test unreadable.
+	private final String setstr = "{\"MOPACSettings\":{\"participants\":["
+			+ "{\"party\":{\"partyref\":\"http://party1\",\"parameters\":{}},\"profile\":\"http://profile1\"},"
+			+ "{\"party\":{\"partyref\":\"http://party2\",\"parameters\":{}},\"profile\":\"http://profile2\"},"
+			+ "{\"party\":{\"partyref\":\"http://party3\",\"parameters\":{}},\"profile\":\"http://profile3\"}],"
+			+ "\"deadline\":{\"deadlinetime\":{\"durationms\":100}},\"votingevaluator\":{\"LargestAgreement\":{}}}}";
+
+	private MOPACSettings settings;
+	private Reporter logger = mock(Reporter.class);
+	private MOPAC mopac;
+
+	private final ProtocolToPartyConnFactory factory = mock(
+			ProtocolToPartyConnFactory.class);
+	private final ProtocolToPartyConn conn1 = mock(ProtocolToPartyConn.class),
+			conn2 = mock(ProtocolToPartyConn.class),
+			conn3 = mock(ProtocolToPartyConn.class);
+
+	@Before
+	public void before() throws JsonParseException, JsonMappingException,
+			IOException, NoResourcesNowException {
+		settings = jackson.readValue(setstr, MOPACSettings.class);
+		mopac = settings.getProtocol(logger);
+
+		List connections = Arrays.asList(conn1, conn2, conn3);
+		when(factory.connect(any(List.class))).thenReturn(connections);
+
+		// hack the state.with(connection) stuff simplistically
+
+		when(conn1.getParty()).thenReturn(PARTY1ID);
+		when(conn2.getParty()).thenReturn(PARTY2ID);
+		when(conn3.getParty()).thenReturn(PARTY3ID);
+	}
+
+	@Test
+	public void testSmoke() {
+	}
+
+	@Test
+	public void testGetDescr() {
+		assertNotNull(mopac.getDescription());
+	}
+
+	@Test
+	public void testInitialState() {
+		MOPACState state = mopac.getState();
+		assertEquals(settings, state.getSettings());
+		assertTrue(state.getActions().isEmpty());
+	}
+
+	@Test
+	public void testProtocol() {
+		assertEquals("MOPAC", mopac.getRef().getURI().getPath());
+	}
+
+	@Ignore
+	@Test
+	public void testWorkingCheckDeadline()
+			throws IOException, NoResourcesNowException, InterruptedException {
+
+		mopac.start(factory);
+
+		System.out.println(mopac.getState());
+
+		// we have deadline in 100ms so check
+		Thread.sleep(200);
+
+		System.out.println(mopac.getState());
+		assertTrue(mopac.getState().isFinal(System.currentTimeMillis()));
+	}
+
+	@Ignore
+	@Test
+	public void testSubscribedToConnections() {
+		mopac.start(factory);
+		verify(conn1, times(1)).addListener(any());
+		verify(conn2, times(1)).addListener(any());
+		verify(conn3, times(1)).addListener(any());
+	}
+
+	@Ignore
+	@Test
+	public void testActionIsBroadcast()
+			throws IOException, NoResourcesNowException, InterruptedException {
+
+		mopac.start(factory);
+		Action offer = mock(Offer.class);
+		mopac.actionRequest(conn1, offer, 1l);
+
+		// 1 time for YourTurn, one time for ActionDone.
+		// but filtering ActionDone here (instead of Action) does not work,
+		// maybe bug in Mockito?
+		verify(conn1, times(2)).send(any(Inform.class));
+	}
+
+	@Test
+	public void testAgreementTerminates() {
+
+	}
+}
Index: protocol/src/test/java/geniusweb/protocol/session/mopac/PartyStatesTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/mopac/PartyStatesTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/session/mopac/PartyStatesTest.java	(revision 21)
@@ -0,0 +1,57 @@
+package geniusweb.protocol.session.mopac;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.junit.Test;
+
+import geniusweb.actions.PartyId;
+import geniusweb.inform.Agreements;
+import geniusweb.protocol.ProtocolException;
+
+public class PartyStatesTest {
+
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+
+	private final Map<PartyId, Integer> powers = new HashMap<>();
+	private final PartyStates states;
+
+	public PartyStatesTest() {
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		powers.put(party3, 3);
+		states = new PartyStates(powers);
+	}
+
+	@Test
+	public void testBasics() {
+		assertEquals(powers.keySet(), states.getNegotiatingParties());
+		assertEquals(powers.keySet(), states.getNotYetActed());
+		assertEquals(0, states.getExceptions().size());
+	}
+
+	@Test
+	public void testException() {
+		PartyStates newstates = states
+				.with(new ProtocolException("bla", party1));
+		assertEquals(new Agreements(), newstates.getAgreements());
+		assertEquals(powers.keySet(), states.getNegotiatingParties());
+		assertEquals(new HashSet<>(Arrays.asList(party2, party3)),
+				newstates.getNotYetActed());
+	}
+
+	@Test
+	public void testFinish() {
+		PartyStates newstates = states.finish();
+		assertEquals(new Agreements(), newstates.getAgreements());
+		assertEquals(powers.keySet(), states.getNegotiatingParties());
+		assertEquals(Collections.emptySet(), newstates.getNotYetActed());
+	}
+}
Index: protocol/src/test/java/geniusweb/protocol/session/mopac/phase/OfferPhaseTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/mopac/phase/OfferPhaseTest.java	(revision 21)
+++ protocol/src/test/java/geniusweb/protocol/session/mopac/phase/OfferPhaseTest.java	(revision 21)
@@ -0,0 +1,67 @@
+package geniusweb.protocol.session.mopac.phase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import geniusweb.actions.EndNegotiation;
+import geniusweb.actions.PartyId;
+import geniusweb.actions.Votes;
+import geniusweb.protocol.ProtocolException;
+import geniusweb.protocol.session.mopac.PartyStates;
+import geniusweb.voting.VotingEvaluator;
+
+/**
+ * We also test defaultPhase here.
+ */
+public class OfferPhaseTest {
+
+	private final PartyId party1 = new PartyId("party1");
+	private final PartyId party2 = new PartyId("party2");
+	private final PartyId party3 = new PartyId("party3");
+
+	private final Map<PartyId, Integer> powers = new HashMap<>();
+	private final PartyStates states;
+	private final VotingEvaluator evaluator = mock(VotingEvaluator.class);
+	private final OfferPhase phase;
+
+	public OfferPhaseTest() {
+		powers.put(party1, 2);
+		powers.put(party2, 3);
+		powers.put(party3, 3);
+		states = new PartyStates(powers);
+		phase = new OfferPhase(null, states, 10l, evaluator);
+	}
+
+	@Test
+	public void isInitFinalTest() {
+		assertFalse(phase.isFinal(1l));
+		assertTrue(phase.isFinal(10l));
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkIncorrectActorTest() throws ProtocolException {
+		phase.checkAction(party1, new EndNegotiation(party2), 1);
+	}
+
+	@Test(expected = ProtocolException.class)
+	public void checkNotAllowedActionTest() throws ProtocolException {
+		phase.checkAction(party1, new Votes(party2, Collections.emptyList()),
+				1);
+	}
+
+	@Test
+	public void testException() {
+		OfferPhase newphase = phase.with(new ProtocolException("bla", party1));
+		assertEquals(10l, newphase.getDeadline());
+		assertEquals(0l, phase.getPartyStates().getActions().size());
+	}
+
+}
Index: protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/session/saop/SAOPSettingsTest.java	(revision 21)
@@ -19,4 +19,5 @@
 import geniusweb.deadline.DeadlineRounds;
 import geniusweb.deadline.DeadlineTime;
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.SessionSettings;
 import geniusweb.references.Parameters;
@@ -29,10 +30,10 @@
 public class SAOPSettingsTest extends GeneralTests<SAOPSettings> {
 
-	private SaopPartyWithProfile partyprof1 = mock(SaopPartyWithProfile.class);
-	private SaopPartyWithProfile partyprof2 = mock(SaopPartyWithProfile.class);
-	private SaopPartyWithProfile partyprof3 = mock(SaopPartyWithProfile.class);
-	private List<SaopPartyWithProfile> participants2 = Arrays.asList(partyprof1,
+	private OnePartyTeam partyprof1 = mock(OnePartyTeam.class);
+	private OnePartyTeam partyprof2 = mock(OnePartyTeam.class);
+	private OnePartyTeam partyprof3 = mock(OnePartyTeam.class);
+	private List<OnePartyTeam> participants2 = Arrays.asList(partyprof1,
 			partyprof2);
-	private List<SaopPartyWithProfile> participants3 = Arrays.asList(partyprof1,
+	private List<OnePartyTeam> participants3 = Arrays.asList(partyprof1,
 			partyprof2, partyprof3);
 
@@ -66,9 +67,7 @@
 				new PartyRef("http://party2"), new Parameters());
 		ProfileRef profile2 = new ProfileRef("http://profile2");
-		SaopPartyWithProfile partywithprof1 = new SaopPartyWithProfile(party1,
-				profile1);
-		SaopPartyWithProfile partywithprof2 = new SaopPartyWithProfile(party2,
-				profile2);
-		List<SaopPartyWithProfile> participants = Arrays.asList(partywithprof1,
+		OnePartyTeam partywithprof1 = new OnePartyTeam(party1, profile1);
+		OnePartyTeam partywithprof2 = new OnePartyTeam(party2, profile2);
+		List<OnePartyTeam> participants = Arrays.asList(partywithprof1,
 				partywithprof2);
 
Index: protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/session/saop/SAOPStateTest.java	(revision 21)
@@ -176,5 +176,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -184,5 +184,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -192,5 +192,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -200,5 +200,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(bid1, state.getAgreement());
+		assertEquals(bid1, state.getAgreements().getMap().get(party1));
 		assertTrue(state.isFinal(NOW));
 	}
@@ -209,5 +209,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -217,5 +217,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -246,5 +246,5 @@
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
 				settings, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 		assertEquals(null, state.getError());
 		assertTrue(state.isFinal(NOW));
@@ -255,5 +255,6 @@
 	public void isProtocolErrorFinal() {
 		SAOPState state = new SAOPState(actions, connections3, progressrounds,
-				settings, null, new ProtocolException("test error", "test"));
+				settings, null,
+				new ProtocolException("test error", new PartyId("test")));
 		assertTrue(state.isFinal(NOW));
 		assertNotNull(state.getError());
@@ -309,5 +310,5 @@
 		assertTrue(state.isFinal(NOW));
 		assertNull(state.getError());
-		assertEquals(bid1, state.getAgreement());
+		assertEquals(bid1, state.getAgreements().getMap().get(party1));
 
 	}
@@ -325,5 +326,5 @@
 		assertTrue(state.isFinal(NOW));
 		assertNull(state.getError());
-		assertEquals(bid2, state.getAgreement());
+		assertEquals(bid2, state.getAgreements().getMap().get(party1));
 
 	}
Index: protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/session/saop/SAOPTest.java	(revision 21)
@@ -34,8 +34,8 @@
 import geniusweb.events.CurrentState;
 import geniusweb.events.ProtocolEvent;
-import geniusweb.party.inform.Finished;
-import geniusweb.party.inform.Inform;
-import geniusweb.party.inform.Settings;
-import geniusweb.party.inform.YourTurn;
+import geniusweb.inform.Finished;
+import geniusweb.inform.Inform;
+import geniusweb.inform.Settings;
+import geniusweb.inform.YourTurn;
 import geniusweb.progress.Progress;
 import geniusweb.protocol.ProtocolException;
@@ -43,4 +43,5 @@
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory;
 import geniusweb.protocol.partyconnection.ProtocolToPartyConnections;
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
 import geniusweb.references.Parameters;
@@ -75,6 +76,6 @@
 	private final PartyRef party2ref = mock(PartyRef.class);
 	private final SAOPSettings settings = mock(SAOPSettings.class);
-	private final SaopPartyWithProfile team1 = mock(SaopPartyWithProfile.class);
-	private final SaopPartyWithProfile team2 = mock(SaopPartyWithProfile.class);
+	private final OnePartyTeam team1 = mock(OnePartyTeam.class);
+	private final OnePartyTeam team2 = mock(OnePartyTeam.class);
 	private SAOP saop;
 	private ProtocolToPartyConnFactory factory;
@@ -82,5 +83,5 @@
 	private ProtocolToPartyConn conn1 = mock(ProtocolToPartyConn.class),
 			conn2 = mock(ProtocolToPartyConn.class);
-	private Map<PartyId, SaopPartyWithProfile> partyprofiles;
+	private Map<PartyId, OnePartyTeam> partyprofiles;
 	private Progress progress = mock(Progress.class);
 	private ProfileRef profile1;
Index: protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/session/saop/SaopPartyWithProfileTest.java	(revision 21)
@@ -17,4 +17,5 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import geniusweb.protocol.session.OnePartyTeam;
 import geniusweb.protocol.session.TeamOfPartiesAndProfiles;
 import geniusweb.references.Parameters;
@@ -24,11 +25,10 @@
 import tudelft.utilities.junit.GeneralTests;
 
-public class SaopPartyWithProfileTest
-		extends GeneralTests<SaopPartyWithProfile> {
+public class SaopPartyWithProfileTest extends GeneralTests<OnePartyTeam> {
 
 	private PartyRef party1, party1a, party2, party3;
 	private ProfileRef profile1, profile2;
-	private SaopPartyWithProfile partyprof1, partyprof1a, partyprof2,
-			partyprof3, partyprof4;
+	private OnePartyTeam partyprof1, partyprof1a, partyprof2, partyprof3,
+			partyprof4;
 	private final String serialized = "{\"party\":{\"partyref\":\"ws:party1\",\"parameters\":{}},\"profile\":\"ws:profile1\"}";
 
@@ -54,14 +54,14 @@
 				settings1);
 
-		partyprof1 = new SaopPartyWithProfile(party1withparams1, profile1);
-		partyprof1a = new SaopPartyWithProfile(party1withparams1, profile1);
-		partyprof2 = new SaopPartyWithProfile(party2withparams1, profile1);
-		partyprof3 = new SaopPartyWithProfile(party1withparams1, profile2);
-		partyprof4 = new SaopPartyWithProfile(party1withparams2, profile1);
+		partyprof1 = new OnePartyTeam(party1withparams1, profile1);
+		partyprof1a = new OnePartyTeam(party1withparams1, profile1);
+		partyprof2 = new OnePartyTeam(party2withparams1, profile1);
+		partyprof3 = new OnePartyTeam(party1withparams1, profile2);
+		partyprof4 = new OnePartyTeam(party1withparams2, profile1);
 
 	}
 
 	@Override
-	public List<List<SaopPartyWithProfile>> getGeneralTestData() {
+	public List<List<OnePartyTeam>> getGeneralTestData() {
 		return Arrays.asList(Arrays.asList(partyprof1, partyprof1a),
 				Arrays.asList(partyprof2), Arrays.asList(partyprof3),
@@ -84,5 +84,5 @@
 	@Test(expected = IllegalArgumentException.class)
 	public void nullTest() throws URISyntaxException {
-		new SaopPartyWithProfile((PartyWithParameters) null, (ProfileRef) null);
+		new OnePartyTeam((PartyWithParameters) null, (ProfileRef) null);
 	}
 
@@ -103,5 +103,5 @@
 		// and therfore can not serialize as TeamOfPartiesAndProfiles
 		TeamOfPartiesAndProfiles p = jackson.readValue(serialized,
-				SaopPartyWithProfile.class);
+				OnePartyTeam.class);
 		System.out.println(p);
 		assertEquals(partyprof1, p);
Index: protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/session/shaop/SHAOPStateTest.java	(revision 21)
@@ -4,5 +4,4 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -194,5 +193,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertNull(state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -201,6 +200,6 @@
 		List<Action> actions = Arrays.asList(offer1, accept2);
 		SHAOPState state = new SHAOPState(actions, connections4, progressrounds,
-				settings, null, 0, null, null);
-		assertNotNull(state.getAgreement());
+				settings, null, 0, partynrs, null);
+		assertFalse(state.getAgreements().getMap().isEmpty());
 		assertTrue(state.isFinal(NOW));
 	}
@@ -211,5 +210,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertNull(state.getAgreement());
+		assert (state.getAgreements().getMap().isEmpty());
 	}
 
@@ -219,5 +218,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 	}
 
@@ -245,5 +244,5 @@
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
 				settings, null, 0, null, null);
-		assertEquals(null, state.getAgreement());
+		assertTrue(state.getAgreements().getMap().isEmpty());
 		assertEquals(null, state.getError());
 		assertTrue(state.isFinal(NOW));
@@ -254,6 +253,7 @@
 	public void isProtocolErrorFinal() {
 		SHAOPState state = new SHAOPState(actions, connections3, progressrounds,
-				settings, new ProtocolException("test error", "test"), 0, null,
-				null);
+				settings,
+				new ProtocolException("test error", new PartyId("test")), 0,
+				null, null);
 		assertTrue(state.isFinal(NOW));
 		assertNotNull(state.getError());
Index: protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsProtocolTest.java	(revision 21)
@@ -20,5 +20,5 @@
 
 import geniusweb.events.ProtocolEvent;
-import geniusweb.issuevalue.Bid;
+import geniusweb.inform.Agreements;
 import geniusweb.protocol.CurrentNegoState;
 import geniusweb.protocol.NegoState;
@@ -44,5 +44,5 @@
 	private final SessionSettings settings = mock(SessionSettings.class);
 	private final SessionState finalsessionstate = mock(SessionState.class);
-	private Bid agreement = mock(Bid.class);
+	private Agreements agreement = mock(Agreements.class);
 	private final long NOW = 1000;
 	private SessionResult finalstate = mock(SessionResult.class);
@@ -53,7 +53,7 @@
 		when(finalsessionstate.isFinal(anyLong())).thenReturn(true);
 		when(finalsessionstate.getSettings()).thenReturn(settings);
-		when(finalsessionstate.getAgreement()).thenReturn(agreement);
+		when(finalsessionstate.getAgreements()).thenReturn(agreement);
 		when(finalsessionstate.getResult()).thenReturn(finalstate);
-		when(finalstate.getAgreement()).thenReturn(agreement);
+		when(finalstate.getAgreements()).thenReturn(agreement);
 		when(state.with(any())).thenReturn(newstate);
 	}
@@ -112,5 +112,5 @@
 				.forClass(SessionResult.class);
 		verify(state, times(1)).with(argument.capture());
-		assertEquals(agreement, argument.getValue().getAgreement());
+		assertEquals(agreement, argument.getValue().getAgreements());
 		assertNull(argument.getValue().getError());
 	}
Index: protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java
===================================================================
--- protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java	(revision 20)
+++ protocol/src/test/java/geniusweb/protocol/tournament/allpermutations/AllPermutationsStateTest.java	(revision 21)
@@ -88,5 +88,4 @@
 		AllPermutationsState state1 = state.with(result);
 
-		assertEquals(null, state1.getError());
 		assertEquals(8, state1.getResults().size());
 		assertEquals(toursettings, state1.getSettings());
