Merge remote-tracking branch 'origin/master'
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentIdCompleter.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentIdCompleter.java
index 5e217d6..5d2e952 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/IntentIdCompleter.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentIdCompleter.java
@@ -24,7 +24,7 @@
         Iterator<Intent> it = service.getIntents().iterator();
         SortedSet<String> strings = delegate.getStrings();
         while (it.hasNext()) {
-            strings.add(it.next().getId().toString());
+            strings.add(it.next().id().toString());
         }
 
         // Now let the completer do the work for figuring out what to offer.
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
index a7d260d..a0c9845 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
@@ -17,8 +17,8 @@
     protected void execute() {
         IntentService service = get(IntentService.class);
         for (Intent intent : service.getIntents()) {
-            IntentState state = service.getIntentState(intent.getId());
-            print("%s %s %s", intent.getId(), state, intent);
+            IntentState state = service.getIntentState(intent.id());
+            print("%s %s %s", intent.id(), state, intent);
         }
     }
 
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
index 3f90f2a..766a849 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/WipeOutCommand.java
@@ -34,7 +34,7 @@
 
         IntentService intentService = get(IntentService.class);
         for (Intent intent : intentService.getIntents()) {
-            if (intentService.getIntentState(intent.getId()) == IntentState.INSTALLED) {
+            if (intentService.getIntentState(intent.id()) == IntentState.INSTALLED) {
                 intentService.withdraw(intent);
             }
         }
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java
index eefe750..c8a4a05 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/AbstractIntent.java
@@ -24,7 +24,7 @@
     }
 
     @Override
-    public IntentId getId() {
+    public IntentId id() {
         return id;
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
index 70cec58..ed0c5cc 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
@@ -53,7 +53,7 @@
      *
      * @return traffic match
      */
-    public TrafficSelector getTrafficSelector() {
+    public TrafficSelector selector() {
         return selector;
     }
 
@@ -62,7 +62,7 @@
      *
      * @return applied action
      */
-    public TrafficTreatment getTrafficTreatment() {
+    public TrafficTreatment treatment() {
         return treatment;
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
index 7cef3da..f420fc2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
@@ -80,9 +80,9 @@
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("id", getId())
-                .add("selector", getTrafficSelector())
-                .add("treatment", getTrafficTreatment())
+                .add("id", id())
+                .add("selector", selector())
+                .add("treatment", treatment())
                 .add("one", one)
                 .add("two", two)
                 .toString();
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
index d4c630a..3e339d1 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
@@ -11,5 +11,5 @@
      *
      * @return intent identifier
      */
-    IntentId getId();
+    IntentId id();
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
index c98e788..742a590 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
@@ -1,106 +1,55 @@
 package org.onlab.onos.net.intent;
 
-import com.google.common.base.MoreObjects;
 import org.onlab.onos.event.AbstractEvent;
 
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
 /**
  * A class to represent an intent related event.
  */
-public class IntentEvent extends AbstractEvent<IntentState, Intent> {
+public class IntentEvent extends AbstractEvent<IntentEvent.Type, Intent> {
 
-    // TODO: determine a suitable parent class; if one does not exist, consider
-    // introducing one
+    public enum Type {
+        /**
+         * Signifies that a new intent has been submitted to the system.
+         */
+        SUBMITTED,
 
-    private final long time;
-    private final Intent intent;
-    private final IntentState state;
-    private final IntentState previous;
+        /**
+         * Signifies that an intent has been successfully installed.
+         */
+        INSTALLED,
 
-    /**
-     * Creates an event describing a state change of an intent.
-     *
-     * @param intent   subject intent
-     * @param state    new intent state
-     * @param previous previous intent state
-     * @param time     time the event created in milliseconds since start of epoch
-     * @throws NullPointerException if the intent or state is null
-     */
-    public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) {
-        super(state, intent);
-        this.intent = checkNotNull(intent);
-        this.state = checkNotNull(state);
-        this.previous = previous;
-        this.time = time;
+        /**
+         * Signifies that an intent has failed compilation or installation.
+         */
+        FAILED,
+
+        /**
+         * Signifies that an intent has been withdrawn from the system.
+         */
+        WITHDRAWN
     }
 
     /**
-     * Returns the state of the intent which caused the event.
+     * Creates an event of a given type and for the specified intent and the
+     * current time.
      *
-     * @return the state of the intent
+     * @param type   event type
+     * @param intent subject intent
+     * @param time   time the event created in milliseconds since start of epoch
      */
-    public IntentState getState() {
-        return state;
+    public IntentEvent(Type type, Intent intent, long time) {
+        super(type, intent, time);
     }
 
     /**
-     * Returns the previous state of the intent which caused the event.
+     * Creates an event of a given type and for the specified intent and the
+     * current time.
      *
-     * @return the previous state of the intent
+     * @param type   event type
+     * @param intent subject intent
      */
-    public IntentState getPreviousState() {
-        return previous;
+    public IntentEvent(Type type, Intent intent) {
+        super(type, intent);
     }
 
-    /**
-     * Returns the intent associated with the event.
-     *
-     * @return the intent
-     */
-    public Intent getIntent() {
-        return intent;
-    }
-
-    /**
-     * Returns the time at which the event was created.
-     *
-     * @return the time in milliseconds since start of epoch
-     */
-    public long getTime() {
-        return time;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        IntentEvent that = (IntentEvent) o;
-        return Objects.equals(this.intent, that.intent)
-                && Objects.equals(this.state, that.state)
-                && Objects.equals(this.previous, that.previous)
-                && Objects.equals(this.time, that.time);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(intent, state, previous, time);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("intent", intent)
-                .add("state", state)
-                .add("previous", previous)
-                .add("time", time)
-                .toString();
-    }
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java
index 20476e5..bd140af 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentState.java
@@ -1,55 +1,70 @@
 package org.onlab.onos.net.intent;
 
 /**
- * This class represents the states of an intent.
- *
- * <p>
- * Note: The state is expressed as enum, but there is possibility
- * in the future that we define specific class instead of enum to improve
- * the extensibility of state definition.
- * </p>
+ * Representation of the phases an intent may attain during its lifecycle.
  */
 public enum IntentState {
-    // FIXME: requires discussion on State vs. EventType and a solid state-transition diagram
-    // TODO: consider the impact of conflict detection
-    // TODO: consider the impact that external events affect an installed intent
+
     /**
-     * The beginning state.
-     *
+     * Signifies that the intent has been submitted and will start compiling
+     * shortly. However, this compilation may not necessarily occur on the
+     * local controller instance.
+     * <p/>
      * All intent in the runtime take this state first.
      */
     SUBMITTED,
 
     /**
-     * The intent compilation has been completed.
-     *
-     * An intent translation graph (tree) is completely created.
-     * Leaves of the graph are installable intent type.
+     * Signifies that the intent is being compiled into installable intents.
+     * This is a transitional state after which the intent will enter either
+     * {@link #FAILED} state or {@link #INSTALLING} state.
      */
-    COMPILED,
+    COMPILING,
 
     /**
-     * The intent has been successfully installed.
+     * Signifies that the resulting installable intents are being installed
+     * into the network environment. This is a transitional state after which
+     * the intent will enter either {@link #INSTALLED} state or
+     * {@link #RECOMPILING} state.
+     */
+    INSTALLING,
+
+    /**
+     * The intent has been successfully installed. This is a state where the
+     * intent may remain parked until it is withdrawn by the application or
+     * until the network environment changes in some way to make the original
+     * set of installable intents untenable.
      */
     INSTALLED,
 
     /**
-     * The intent is being withdrawn.
-     *
-     * When {@link IntentService#withdraw(Intent)} is called,
-     * the intent takes this state first.
+     * Signifies that the intent is being recompiled into installable intents
+     * as an attempt to adapt to an anomaly in the network environment.
+     * This is a transitional state after which the intent will enter either
+     * {@link #FAILED} state or {@link #INSTALLING} state.
+     * <p/>
+     * Exit to the {@link #FAILED} state may be caused by failure to compile
+     * or by compiling into the same set of installable intents which have
+     * previously failed to be installed.
+     */
+    RECOMPILING,
+
+    /**
+     * Indicates that the intent is being withdrawn. This is a transitional
+     * state, triggered by invocation of the
+     * {@link IntentService#withdraw(Intent)} but one with only one outcome,
+     * which is the the intent being placed in the {@link #WITHDRAWN} state.
      */
     WITHDRAWING,
 
     /**
-     * The intent has been successfully withdrawn.
+     * Indicates that the intent has been successfully withdrawn.
      */
     WITHDRAWN,
 
     /**
-     * The intent has failed to be compiled, installed, or withdrawn.
-     *
-     * When the intent failed to be withdrawn, it is still, at least partially installed.
+     * Signifies that the intent has failed compiling, installing or
+     * recompiling states.
      */
-    FAILED,
+    FAILED
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java
index 037f179..fc023bb 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java
@@ -10,10 +10,16 @@
 public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
 
     /**
-     * Creates a new intent.
+     * Submits a new intent into the store. If the returned event is not
+     * null, the manager is expected to dispatch the event and then to kick
+     * off intent compilation process. Otherwise, another node has been elected
+     * to perform the compilation process and the node will learn about
+     * the submittal and results of the intent compilation via the delegate
+     * mechanism.
      *
-     * @param intent intent
-     * @return appropriate event or null if no change resulted
+     * @param intent intent to be submitted
+     * @return event indicating the intent was submitted or null if no
+     * change resulted, e.g. duplicate intent
      */
     IntentEvent createIntent(Intent intent);
 
@@ -68,10 +74,9 @@
      *
      * @param intentId           original intent identifier
      * @param installableIntents compiled installable intents
-     * @return compiled state transition event
      */
-    IntentEvent addInstallableIntents(IntentId intentId,
-                                      List<InstallableIntent> installableIntents);
+    void addInstallableIntents(IntentId intentId,
+                               List<InstallableIntent> installableIntents);
 
     /**
      * Returns the list of the installable events associated with the specified
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
index af1e84b..be8d309 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
@@ -1,25 +1,24 @@
 package org.onlab.onos.net.intent;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-import java.util.Set;
-
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Sets;
 import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.flow.TrafficSelector;
 import org.onlab.onos.net.flow.TrafficTreatment;
 
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.Sets;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Abstraction of multiple source to single destination connectivity intent.
  */
 public class MultiPointToSinglePointIntent extends ConnectivityIntent {
 
-    private final Set<ConnectPoint> ingressPorts;
-    private final ConnectPoint egressPort;
+    private final Set<ConnectPoint> ingressPoints;
+    private final ConnectPoint egressPoint;
 
     /**
      * Creates a new multi-to-single point connectivity intent for the specified
@@ -28,25 +27,25 @@
      * @param id           intent identifier
      * @param match        traffic match
      * @param action       action
-     * @param ingressPorts set of ports from which ingress traffic originates
-     * @param egressPort   port to which traffic will egress
-     * @throws NullPointerException     if {@code ingressPorts} or
-     *                                  {@code egressPort} is null.
-     * @throws IllegalArgumentException if the size of {@code ingressPorts} is
+     * @param ingressPoints set of ports from which ingress traffic originates
+     * @param egressPoint   port to which traffic will egress
+     * @throws NullPointerException     if {@code ingressPoints} or
+     *                                  {@code egressPoint} is null.
+     * @throws IllegalArgumentException if the size of {@code ingressPoints} is
      *                                  not more than 1
      */
     public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match,
                                          TrafficTreatment action,
-                                         Set<ConnectPoint> ingressPorts,
-                                         ConnectPoint egressPort) {
+                                         Set<ConnectPoint> ingressPoints,
+                                         ConnectPoint egressPoint) {
         super(id, match, action);
 
-        checkNotNull(ingressPorts);
-        checkArgument(!ingressPorts.isEmpty(),
+        checkNotNull(ingressPoints);
+        checkArgument(!ingressPoints.isEmpty(),
                       "there should be at least one ingress port");
 
-        this.ingressPorts = Sets.newHashSet(ingressPorts);
-        this.egressPort = checkNotNull(egressPort);
+        this.ingressPoints = Sets.newHashSet(ingressPoints);
+        this.egressPoint = checkNotNull(egressPoint);
     }
 
     /**
@@ -54,8 +53,8 @@
      */
     protected MultiPointToSinglePointIntent() {
         super();
-        this.ingressPorts = null;
-        this.egressPort = null;
+        this.ingressPoints = null;
+        this.egressPoint = null;
     }
 
     /**
@@ -64,8 +63,8 @@
      *
      * @return set of ingress ports
      */
-    public Set<ConnectPoint> getIngressPorts() {
-        return ingressPorts;
+    public Set<ConnectPoint> ingressPoints() {
+        return ingressPoints;
     }
 
     /**
@@ -73,8 +72,8 @@
      *
      * @return egress port
      */
-    public ConnectPoint getEgressPort() {
-        return egressPort;
+    public ConnectPoint egressPoint() {
+        return egressPoint;
     }
 
     @Override
@@ -90,23 +89,23 @@
         }
 
         MultiPointToSinglePointIntent that = (MultiPointToSinglePointIntent) o;
-        return Objects.equals(this.ingressPorts, that.ingressPorts)
-                && Objects.equals(this.egressPort, that.egressPort);
+        return Objects.equals(this.ingressPoints, that.ingressPoints)
+                && Objects.equals(this.egressPoint, that.egressPoint);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), ingressPorts, egressPort);
+        return Objects.hash(super.hashCode(), ingressPoints, egressPoint);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("id", getId())
-                .add("match", getTrafficSelector())
-                .add("action", getTrafficTreatment())
-                .add("ingressPorts", getIngressPorts())
-                .add("egressPort", getEgressPort())
+                .add("id", id())
+                .add("match", selector())
+                .add("action", treatment())
+                .add("ingressPoints", ingressPoints())
+                .add("egressPoint", egressPoint())
                 .toString();
     }
 }
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
index 4c3486f..ff2e917 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
@@ -46,7 +46,7 @@
      *
      * @return traversed links
      */
-    public Path getPath() {
+    public Path path() {
         return path;
     }
 
@@ -79,11 +79,11 @@
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("id", getId())
-                .add("match", getTrafficSelector())
-                .add("action", getTrafficTreatment())
-                .add("ingressPort", getIngressPort())
-                .add("egressPort", getEgressPort())
+                .add("id", id())
+                .add("match", selector())
+                .add("action", treatment())
+                .add("ingressPort", ingressPoint())
+                .add("egressPort", egressPoint())
                 .add("path", path)
                 .toString();
     }
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
index 4c86bae..7b7c18a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
@@ -14,27 +14,27 @@
  */
 public class PointToPointIntent extends ConnectivityIntent {
 
-    private final ConnectPoint ingressPort;
-    private final ConnectPoint egressPort;
+    private final ConnectPoint ingressPoint;
+    private final ConnectPoint egressPoint;
 
     /**
      * Creates a new point-to-point intent with the supplied ingress/egress
      * ports.
      *
-     * @param id          intent identifier
-     * @param selector    traffic selector
-     * @param treatment   treatment
-     * @param ingressPort ingress port
-     * @param egressPort  egress port
-     * @throws NullPointerException if {@code ingressPort} or {@code egressPort} is null.
+     * @param id           intent identifier
+     * @param selector     traffic selector
+     * @param treatment    treatment
+     * @param ingressPoint ingress port
+     * @param egressPoint  egress port
+     * @throws NullPointerException if {@code ingressPoint} or {@code egressPoints} is null.
      */
     public PointToPointIntent(IntentId id, TrafficSelector selector,
                               TrafficTreatment treatment,
-                              ConnectPoint ingressPort,
-                              ConnectPoint egressPort) {
+                              ConnectPoint ingressPoint,
+                              ConnectPoint egressPoint) {
         super(id, selector, treatment);
-        this.ingressPort = checkNotNull(ingressPort);
-        this.egressPort = checkNotNull(egressPort);
+        this.ingressPoint = checkNotNull(ingressPoint);
+        this.egressPoint = checkNotNull(egressPoint);
     }
 
     /**
@@ -42,8 +42,8 @@
      */
     protected PointToPointIntent() {
         super();
-        this.ingressPort = null;
-        this.egressPort = null;
+        this.ingressPoint = null;
+        this.egressPoint = null;
     }
 
     /**
@@ -52,8 +52,8 @@
      *
      * @return ingress port
      */
-    public ConnectPoint getIngressPort() {
-        return ingressPort;
+    public ConnectPoint ingressPoint() {
+        return ingressPoint;
     }
 
     /**
@@ -61,8 +61,8 @@
      *
      * @return egress port
      */
-    public ConnectPoint getEgressPort() {
-        return egressPort;
+    public ConnectPoint egressPoint() {
+        return egressPoint;
     }
 
     @Override
@@ -78,23 +78,23 @@
         }
 
         PointToPointIntent that = (PointToPointIntent) o;
-        return Objects.equals(this.ingressPort, that.ingressPort)
-                && Objects.equals(this.egressPort, that.egressPort);
+        return Objects.equals(this.ingressPoint, that.ingressPoint)
+                && Objects.equals(this.egressPoint, that.egressPoint);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), ingressPort, egressPort);
+        return Objects.hash(super.hashCode(), ingressPoint, egressPoint);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("id", getId())
-                .add("match", getTrafficSelector())
-                .add("action", getTrafficTreatment())
-                .add("ingressPort", ingressPort)
-                .add("egressPort", egressPort)
+                .add("id", id())
+                .add("match", selector())
+                .add("action", treatment())
+                .add("ingressPoint", ingressPoint)
+                .add("egressPoints", egressPoint)
                 .toString();
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
index af2616b..2a17bfe 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
@@ -17,34 +17,34 @@
  */
 public class SinglePointToMultiPointIntent extends ConnectivityIntent {
 
-    private final ConnectPoint ingressPort;
-    private final Set<ConnectPoint> egressPorts;
+    private final ConnectPoint ingressPoint;
+    private final Set<ConnectPoint> egressPoints;
 
     /**
      * Creates a new single-to-multi point connectivity intent.
      *
-     * @param id          intent identifier
-     * @param selector    traffic selector
-     * @param treatment   treatment
-     * @param ingressPort port on which traffic will ingress
-     * @param egressPorts set of ports on which traffic will egress
-     * @throws NullPointerException     if {@code ingressPort} or
-     *                                  {@code egressPorts} is null
-     * @throws IllegalArgumentException if the size of {@code egressPorts} is
+     * @param id           intent identifier
+     * @param selector     traffic selector
+     * @param treatment    treatment
+     * @param ingressPoint port on which traffic will ingress
+     * @param egressPoints set of ports on which traffic will egress
+     * @throws NullPointerException     if {@code ingressPoint} or
+     *                                  {@code egressPoints} is null
+     * @throws IllegalArgumentException if the size of {@code egressPoints} is
      *                                  not more than 1
      */
     public SinglePointToMultiPointIntent(IntentId id, TrafficSelector selector,
                                          TrafficTreatment treatment,
-                                         ConnectPoint ingressPort,
-                                         Set<ConnectPoint> egressPorts) {
+                                         ConnectPoint ingressPoint,
+                                         Set<ConnectPoint> egressPoints) {
         super(id, selector, treatment);
 
-        checkNotNull(egressPorts);
-        checkArgument(!egressPorts.isEmpty(),
+        checkNotNull(egressPoints);
+        checkArgument(!egressPoints.isEmpty(),
                       "there should be at least one egress port");
 
-        this.ingressPort = checkNotNull(ingressPort);
-        this.egressPorts = Sets.newHashSet(egressPorts);
+        this.ingressPoint = checkNotNull(ingressPoint);
+        this.egressPoints = Sets.newHashSet(egressPoints);
     }
 
     /**
@@ -52,8 +52,8 @@
      */
     protected SinglePointToMultiPointIntent() {
         super();
-        this.ingressPort = null;
-        this.egressPorts = null;
+        this.ingressPoint = null;
+        this.egressPoints = null;
     }
 
     /**
@@ -61,8 +61,8 @@
      *
      * @return ingress port
      */
-    public ConnectPoint getIngressPort() {
-        return ingressPort;
+    public ConnectPoint ingressPoint() {
+        return ingressPoint;
     }
 
     /**
@@ -70,8 +70,8 @@
      *
      * @return set of egress ports
      */
-    public Set<ConnectPoint> getEgressPorts() {
-        return egressPorts;
+    public Set<ConnectPoint> egressPoints() {
+        return egressPoints;
     }
 
     @Override
@@ -87,23 +87,23 @@
         }
 
         SinglePointToMultiPointIntent that = (SinglePointToMultiPointIntent) o;
-        return Objects.equals(this.ingressPort, that.ingressPort)
-                && Objects.equals(this.egressPorts, that.egressPorts);
+        return Objects.equals(this.ingressPoint, that.ingressPoint)
+                && Objects.equals(this.egressPoints, that.egressPoints);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), ingressPort, egressPorts);
+        return Objects.hash(super.hashCode(), ingressPoint, egressPoints);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(getClass())
-                .add("id", getId())
-                .add("match", getTrafficSelector())
-                .add("action", getTrafficTreatment())
-                .add("ingressPort", ingressPort)
-                .add("egressPort", egressPorts)
+                .add("id", id())
+                .add("match", selector())
+                .add("action", treatment())
+                .add("ingressPoint", ingressPoint)
+                .add("egressPort", egressPoints)
                 .toString();
     }
 
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
index 349749e..9afccc8 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
@@ -40,8 +40,7 @@
             @Override
             public void run() {
                 try {
-                    List<InstallableIntent> installable = compileIntent(intent);
-                    installIntents(intent, installable);
+                    executeCompilingPhase(intent);
                 } catch (IntentException e) {
                     exceptions.add(e);
                 }
@@ -55,8 +54,8 @@
             @Override
             public void run() {
                 try {
-                    List<InstallableIntent> installable = getInstallable(intent.getId());
-                    uninstallIntents(intent, installable);
+                    List<InstallableIntent> installable = getInstallable(intent.id());
+                    executeWithdrawingPhase(intent, installable);
                 } catch (IntentException e) {
                     exceptions.add(e);
                 }
@@ -84,53 +83,60 @@
         return installer;
     }
 
-    private <T extends Intent> List<InstallableIntent> compileIntent(T intent) {
+    private <T extends Intent> void executeCompilingPhase(T intent) {
+        setState(intent, IntentState.COMPILING);
         try {
             // For the fake, we compile using a single level pass
             List<InstallableIntent> installable = new ArrayList<>();
             for (Intent compiled : getCompiler(intent).compile(intent)) {
                 installable.add((InstallableIntent) compiled);
             }
-            setState(intent, IntentState.COMPILED);
-            return installable;
+            executeInstallingPhase(intent, installable);
+
         } catch (IntentException e) {
             setState(intent, IntentState.FAILED);
-            throw e;
+            dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
         }
     }
 
-    private void installIntents(Intent intent, List<InstallableIntent> installable) {
+    private void executeInstallingPhase(Intent intent,
+                                        List<InstallableIntent> installable) {
+        setState(intent, IntentState.INSTALLING);
         try {
             for (InstallableIntent ii : installable) {
                 registerSubclassInstallerIfNeeded(ii);
                 getInstaller(ii).install(ii);
             }
             setState(intent, IntentState.INSTALLED);
-            putInstallable(intent.getId(), installable);
+            putInstallable(intent.id(), installable);
+            dispatch(new IntentEvent(IntentEvent.Type.INSTALLED, intent));
+
         } catch (IntentException e) {
             setState(intent, IntentState.FAILED);
-            throw e;
+            dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
         }
     }
 
-    private void uninstallIntents(Intent intent, List<InstallableIntent> installable) {
+    private void executeWithdrawingPhase(Intent intent,
+                                         List<InstallableIntent> installable) {
+        setState(intent, IntentState.WITHDRAWING);
         try {
             for (InstallableIntent ii : installable) {
                 getInstaller(ii).uninstall(ii);
             }
+            removeInstallable(intent.id());
             setState(intent, IntentState.WITHDRAWN);
-            removeInstallable(intent.getId());
+            dispatch(new IntentEvent(IntentEvent.Type.WITHDRAWN, intent));
         } catch (IntentException e) {
+            // FIXME: Do we really want to do this?
             setState(intent, IntentState.FAILED);
-            throw e;
+            dispatch(new IntentEvent(IntentEvent.Type.FAILED, intent));
         }
     }
 
     // Sets the internal state for the given intent and dispatches an event
     private void setState(Intent intent, IntentState state) {
-        IntentState previous = intentStates.get(intent.getId());
-        intentStates.put(intent.getId(), state);
-        dispatch(new IntentEvent(intent, state, previous, System.currentTimeMillis()));
+        intentStates.put(intent.id(), state);
     }
 
     private void putInstallable(IntentId id, List<InstallableIntent> installable) {
@@ -152,15 +158,15 @@
 
     @Override
     public void submit(Intent intent) {
-        intents.put(intent.getId(), intent);
+        intents.put(intent.id(), intent);
         setState(intent, IntentState.SUBMITTED);
+        dispatch(new IntentEvent(IntentEvent.Type.SUBMITTED, intent));
         executeSubmit(intent);
     }
 
     @Override
     public void withdraw(Intent intent) {
-        intents.remove(intent.getId());
-        setState(intent, IntentState.WITHDRAWING);
+        intents.remove(intent.id());
         executeWithdraw(intent);
     }
 
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
index 825be86..7eb0e19 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
@@ -10,11 +10,9 @@
 import java.util.Iterator;
 import java.util.List;
 
-import static org.onlab.onos.net.intent.IntentState.*;
 import static org.junit.Assert.*;
+import static org.onlab.onos.net.intent.IntentEvent.Type.*;
 
-// TODO: consider make it categorized as integration test when it become
-//   slow test or fragile test
 /**
  * Suite of tests for the intent service contract.
  */
@@ -64,13 +62,13 @@
         TestTools.assertAfter(GRACE_MS, new Runnable() {
             @Override
             public void run() {
-                assertEquals("incorrect intent state", INSTALLED,
-                             service.getIntentState(intent.getId()));
+                assertEquals("incorrect intent state", IntentState.INSTALLED,
+                             service.getIntentState(intent.id()));
             }
         });
 
         // Make sure that all expected events have been emitted
-        validateEvents(intent, SUBMITTED, COMPILED, INSTALLED);
+        validateEvents(intent, SUBMITTED, INSTALLED);
 
         // Make sure there is just one intent (and is ours)
         assertEquals("incorrect intent count", 1, service.getIntentCount());
@@ -85,19 +83,19 @@
         TestTools.assertAfter(GRACE_MS, new Runnable() {
             @Override
             public void run() {
-                assertEquals("incorrect intent state", WITHDRAWN,
-                             service.getIntentState(intent.getId()));
+                assertEquals("incorrect intent state", IntentState.WITHDRAWN,
+                             service.getIntentState(intent.id()));
             }
         });
 
         // Make sure that all expected events have been emitted
-        validateEvents(intent, WITHDRAWING, WITHDRAWN);
+        validateEvents(intent, WITHDRAWN);
 
         // TODO: discuss what is the fate of intents after they have been withdrawn
         // Make sure that the intent is no longer in the system
 //        assertEquals("incorrect intent count", 0, service.getIntents().size());
-//        assertNull("intent should not be found", service.getIntent(intent.getId()));
-//        assertNull("intent state should not be found", service.getIntentState(intent.getId()));
+//        assertNull("intent should not be found", service.getIntent(intent.id()));
+//        assertNull("intent state should not be found", service.getIntentState(intent.id()));
     }
 
     @Test
@@ -113,8 +111,8 @@
         TestTools.assertAfter(GRACE_MS, new Runnable() {
             @Override
             public void run() {
-                assertEquals("incorrect intent state", FAILED,
-                             service.getIntentState(intent.getId()));
+                assertEquals("incorrect intent state", IntentState.FAILED,
+                             service.getIntentState(intent.id()));
             }
         });
 
@@ -136,13 +134,13 @@
         TestTools.assertAfter(GRACE_MS, new Runnable() {
             @Override
             public void run() {
-                assertEquals("incorrect intent state", FAILED,
-                             service.getIntentState(intent.getId()));
+                assertEquals("incorrect intent state", IntentState.FAILED,
+                             service.getIntentState(intent.id()));
             }
         });
 
         // Make sure that all expected events have been emitted
-        validateEvents(intent, SUBMITTED, COMPILED, FAILED);
+        validateEvents(intent, SUBMITTED, FAILED);
     }
 
     /**
@@ -151,23 +149,23 @@
      * considered.
      *
      * @param intent intent subject
-     * @param states list of states for which events are expected
+     * @param types  list of event types for which events are expected
      */
-    protected void validateEvents(Intent intent, IntentState... states) {
+    protected void validateEvents(Intent intent, IntentEvent.Type... types) {
         Iterator<IntentEvent> events = listener.events.iterator();
-        for (IntentState state : states) {
+        for (IntentEvent.Type type : types) {
             IntentEvent event = events.hasNext() ? events.next() : null;
             if (event == null) {
-                fail("expected event not found: " + state);
-            } else if (intent.equals(event.getIntent())) {
-                assertEquals("incorrect state", state, event.getState());
+                fail("expected event not found: " + type);
+            } else if (intent.equals(event.subject())) {
+                assertEquals("incorrect state", type, event.type());
             }
         }
 
         // Remainder of events should not apply to this intent; make sure.
         while (events.hasNext()) {
             assertFalse("unexpected event for intent",
-                        intent.equals(events.next().getIntent()));
+                        intent.equals(events.next().subject()));
         }
     }
 
@@ -228,8 +226,8 @@
         TestTools.assertAfter(GRACE_MS, new Runnable() {
             @Override
             public void run() {
-                assertEquals("incorrect intent state", INSTALLED,
-                             service.getIntentState(intent.getId()));
+                assertEquals("incorrect intent state", IntentState.INSTALLED,
+                             service.getIntentState(intent.id()));
             }
         });
 
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java
index d971ba2..66d294a 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntentTest.java
@@ -12,10 +12,10 @@
     @Test
     public void basics() {
         MultiPointToSinglePointIntent intent = createOne();
-        assertEquals("incorrect id", IID, intent.getId());
-        assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
-        assertEquals("incorrect ingress", PS1, intent.getIngressPorts());
-        assertEquals("incorrect egress", P2, intent.getEgressPort());
+        assertEquals("incorrect id", IID, intent.id());
+        assertEquals("incorrect match", MATCH, intent.selector());
+        assertEquals("incorrect ingress", PS1, intent.ingressPoints());
+        assertEquals("incorrect egress", P2, intent.egressPoint());
     }
 
     @Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
index bd8dc08..7c15c37 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/PathIntentTest.java
@@ -16,12 +16,12 @@
     @Test
     public void basics() {
         PathIntent intent = createOne();
-        assertEquals("incorrect id", IID, intent.getId());
-        assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
-        assertEquals("incorrect action", NOP, intent.getTrafficTreatment());
-        assertEquals("incorrect ingress", P1, intent.getIngressPort());
-        assertEquals("incorrect egress", P2, intent.getEgressPort());
-        assertEquals("incorrect path", PATH1, intent.getPath());
+        assertEquals("incorrect id", IID, intent.id());
+        assertEquals("incorrect match", MATCH, intent.selector());
+        assertEquals("incorrect action", NOP, intent.treatment());
+        assertEquals("incorrect ingress", P1, intent.ingressPoint());
+        assertEquals("incorrect egress", P2, intent.egressPoint());
+        assertEquals("incorrect path", PATH1, intent.path());
     }
 
     @Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java
index 426a3d9..e0c5562 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/PointToPointIntentTest.java
@@ -12,10 +12,10 @@
     @Test
     public void basics() {
         PointToPointIntent intent = createOne();
-        assertEquals("incorrect id", IID, intent.getId());
-        assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
-        assertEquals("incorrect ingress", P1, intent.getIngressPort());
-        assertEquals("incorrect egress", P2, intent.getEgressPort());
+        assertEquals("incorrect id", IID, intent.id());
+        assertEquals("incorrect match", MATCH, intent.selector());
+        assertEquals("incorrect ingress", P1, intent.ingressPoint());
+        assertEquals("incorrect egress", P2, intent.egressPoint());
     }
 
     @Override
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java
index 0561a87..64c9292 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntentTest.java
@@ -12,10 +12,10 @@
     @Test
     public void basics() {
         SinglePointToMultiPointIntent intent = createOne();
-        assertEquals("incorrect id", IID, intent.getId());
-        assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
-        assertEquals("incorrect ingress", P1, intent.getIngressPort());
-        assertEquals("incorrect egress", PS2, intent.getEgressPorts());
+        assertEquals("incorrect id", IID, intent.id());
+        assertEquals("incorrect match", MATCH, intent.selector());
+        assertEquals("incorrect ingress", P1, intent.ingressPoint());
+        assertEquals("incorrect egress", PS2, intent.egressPoints());
     }
 
     @Override
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
index 541a702..de61e8e 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
@@ -71,11 +71,11 @@
     private Intent createPathIntent(Path path, Host src, Host dst,
                                     HostToHostIntent intent) {
 
-        TrafficSelector selector = builder(intent.getTrafficSelector())
+        TrafficSelector selector = builder(intent.selector())
                 .matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
 
         return new PathIntent(intentIdGenerator.getNewId(),
-                              selector, intent.getTrafficTreatment(),
+                              selector, intent.treatment(),
                               path.src(), path.dst(), path);
     }
 
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
index 6268245..197c2b2 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -28,11 +28,15 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.onos.net.intent.IntentState.*;
+import static org.onlab.util.Tools.namedThreads;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -56,6 +60,8 @@
     private final AbstractListenerRegistry<IntentEvent, IntentListener>
             listenerRegistry = new AbstractListenerRegistry<>();
 
+    private ExecutorService executor = newSingleThreadExecutor(namedThreads("onos-intents"));
+
     private final IntentStoreDelegate delegate = new InternalStoreDelegate();
     private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
 
@@ -63,7 +69,7 @@
     protected IntentStore store;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowTrackerService trackerService;
+    protected ObjectiveTrackerService trackerService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected EventDeliveryService eventDispatcher;
@@ -89,21 +95,16 @@
         checkNotNull(intent, INTENT_NULL);
         registerSubclassCompilerIfNeeded(intent);
         IntentEvent event = store.createIntent(intent);
-        processStoreEvent(event);
+        if (event != null) {
+            eventDispatcher.post(event);
+            executor.execute(new IntentTask(COMPILING, intent));
+        }
     }
 
     @Override
     public void withdraw(Intent intent) {
         checkNotNull(intent, INTENT_NULL);
-        IntentEvent event = store.setState(intent, WITHDRAWING);
-        List<InstallableIntent> installables = store.getInstallableIntents(intent.getId());
-        if (installables != null) {
-            for (InstallableIntent installable : installables) {
-                trackerService.removeTrackedResources(intent.getId(),
-                                                      installable.requiredLinks());
-            }
-        }
-        processStoreEvent(event);
+        executor.execute(new IntentTask(WITHDRAWING, intent));
     }
 
     // FIXME: implement this method
@@ -207,56 +208,122 @@
     }
 
     /**
-     * Compiles an intent.
+     * Compiles the specified intent.
      *
-     * @param intent intent
+     * @param intent intent to be compiled
      */
-    private void compileIntent(Intent intent) {
-        // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
-        // TODO: implement compilation traversing tree structure
+    private void executeCompilingPhase(Intent intent) {
+        // Indicate that the intent is entering the compiling phase.
+        store.setState(intent, COMPILING);
+
+        try {
+            // Compile the intent into installable derivatives.
+            List<InstallableIntent> installable = compileIntent(intent);
+
+            // If all went well, associate the resulting list of installable
+            // intents with the top-level intent and proceed to install.
+            store.addInstallableIntents(intent.id(), installable);
+            executeInstallingPhase(intent);
+
+        } catch (Exception e) {
+            // If compilation failed, mark the intent as failed.
+            store.setState(intent, FAILED);
+        }
+    }
+
+    // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
+    // TODO: implement compilation traversing tree structure
+    private List<InstallableIntent> compileIntent(Intent intent) {
         List<InstallableIntent> installable = new ArrayList<>();
         for (Intent compiled : getCompiler(intent).compile(intent)) {
             InstallableIntent installableIntent = (InstallableIntent) compiled;
             installable.add(installableIntent);
-            trackerService.addTrackedResources(intent.getId(),
+            trackerService.addTrackedResources(intent.id(),
                                                installableIntent.requiredLinks());
         }
-        IntentEvent event = store.addInstallableIntents(intent.getId(), installable);
-        processStoreEvent(event);
+        return installable;
     }
 
     /**
-     * Installs an intent.
+     * Installs all installable intents associated with the specified top-level
+     * intent.
      *
-     * @param intent intent
+     * @param intent intent to be installed
      */
-    private void installIntent(Intent intent) {
-        List<InstallableIntent> installables = store.getInstallableIntents(intent.getId());
-        if (installables != null) {
-            for (InstallableIntent installable : installables) {
-                registerSubclassInstallerIfNeeded(installable);
-                getInstaller(installable).install(installable);
+    private void executeInstallingPhase(Intent intent) {
+        // Indicate that the intent is entering the installing phase.
+        store.setState(intent, INSTALLING);
+
+        try {
+            List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
+            if (installables != null) {
+                for (InstallableIntent installable : installables) {
+                    registerSubclassInstallerIfNeeded(installable);
+                    getInstaller(installable).install(installable);
+                }
             }
-        }
-        IntentEvent event = store.setState(intent, INSTALLED);
-        processStoreEvent(event);
+            eventDispatcher.post(store.setState(intent, INSTALLED));
 
+        } catch (Exception e) {
+            // If compilation failed, kick off the recompiling phase.
+            executeRecompilingPhase(intent);
+        }
     }
 
     /**
-     * Uninstalls an intent.
+     * Recompiles the specified intent.
      *
-     * @param intent intent
+     * @param intent intent to be recompiled
      */
-    private void uninstallIntent(Intent intent) {
-        List<InstallableIntent> installables = store.getInstallableIntents(intent.getId());
+    private void executeRecompilingPhase(Intent intent) {
+        // Indicate that the intent is entering the recompiling phase.
+        store.setState(intent, RECOMPILING);
+
+        try {
+            // Compile the intent into installable derivatives.
+            List<InstallableIntent> installable = compileIntent(intent);
+
+            // If all went well, compare the existing list of installable
+            // intents with the newly compiled list. If they are the same,
+            // bail, out since the previous approach was determined not to
+            // be viable.
+            List<InstallableIntent> originalInstallable =
+                    store.getInstallableIntents(intent.id());
+
+            if (Objects.equals(originalInstallable, installable)) {
+                eventDispatcher.post(store.setState(intent, FAILED));
+            } else {
+                // Otherwise, re-associate the newly compiled installable intents
+                // with the top-level intent and kick off installing phase.
+                store.addInstallableIntents(intent.id(), installable);
+                executeInstallingPhase(intent);
+            }
+        } catch (Exception e) {
+            // If compilation failed, mark the intent as failed.
+            eventDispatcher.post(store.setState(intent, FAILED));
+        }
+    }
+
+    /**
+     * Uninstalls the specified intent by uninstalling all of its associated
+     * installable derivatives.
+     *
+     * @param intent intent to be installed
+     */
+    private void executeWithdrawingPhase(Intent intent) {
+        // Indicate that the intent is being withdrawn.
+        store.setState(intent, WITHDRAWING);
+        List<InstallableIntent> installables = store.getInstallableIntents(intent.id());
         if (installables != null) {
             for (InstallableIntent installable : installables) {
                 getInstaller(installable).uninstall(installable);
             }
         }
-        store.removeInstalledIntents(intent.getId());
-        store.setState(intent, WITHDRAWN);
+
+        // If all went well, disassociate the top-level intent with its
+        // installable derivatives and mark it as withdrawn.
+        store.removeInstalledIntents(intent.id());
+        eventDispatcher.post(store.setState(intent, WITHDRAWN));
     }
 
     /**
@@ -309,55 +376,58 @@
         }
     }
 
-    /**
-     * Handles state transition of submitted intents.
-     */
-    private void processStoreEvent(IntentEvent event) {
-        eventDispatcher.post(event);
-        Intent intent = event.getIntent();
-        try {
-            switch (event.getState()) {
-                case SUBMITTED:
-                    compileIntent(intent);
-                    break;
-                case COMPILED:
-                    installIntent(intent);
-                    break;
-                case INSTALLED:
-                    break;
-                case WITHDRAWING:
-                    uninstallIntent(intent);
-                    break;
-                case WITHDRAWN:
-                    break;
-                case FAILED:
-                    break;
-                default:
-                    throw new IllegalStateException("the state of IntentEvent is illegal: " +
-                                                            event.getState());
-            }
-        } catch (IntentException e) {
-            store.setState(intent, FAILED);
-        }
-
-    }
-
     // Store delegate to re-post events emitted from the store.
     private class InternalStoreDelegate implements IntentStoreDelegate {
         @Override
         public void notify(IntentEvent event) {
-            processStoreEvent(event);
+            eventDispatcher.post(event);
+            if (event.type() == IntentEvent.Type.SUBMITTED) {
+                executor.execute(new IntentTask(COMPILING, event.subject()));
+            }
         }
     }
 
     // Topology change delegate
     private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
         @Override
-        public void bumpIntents(Iterable<IntentId> intentIds) {
+        public void triggerCompile(Iterable<IntentId> intentIds,
+                                   boolean compileAllFailed) {
+            // Attempt recompilation of the specified intents first.
             for (IntentId intentId : intentIds) {
-                compileIntent(getIntent(intentId));
+                executeRecompilingPhase(getIntent(intentId));
+            }
+
+            if (compileAllFailed) {
+                // If required, compile all currently failed intents.
+                for (Intent intent : getIntents()) {
+                    if (getIntentState(intent.id()) == FAILED) {
+                        executeCompilingPhase(intent);
+                    }
+                }
             }
         }
-
     }
+
+    // Auxiliary runnable to perform asynchronous tasks.
+    private class IntentTask implements Runnable {
+        private final IntentState state;
+        private final Intent intent;
+
+        public IntentTask(IntentState state, Intent intent) {
+            this.state = state;
+            this.intent = intent;
+        }
+
+        @Override
+        public void run() {
+            if (state == COMPILING) {
+                executeCompilingPhase(intent);
+            } else if (state == RECOMPILING) {
+                executeRecompilingPhase(intent);
+            } else if (state == WITHDRAWING) {
+                executeWithdrawingPhase(intent);
+            }
+        }
+    }
+
 }
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTracker.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
similarity index 81%
rename from core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTracker.java
rename to core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
index f69bf78..17d420b 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTracker.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTracker.java
@@ -19,12 +19,15 @@
 import org.slf4j.Logger;
 
 import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
 import static org.onlab.util.Tools.namedThreads;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -34,7 +37,7 @@
  */
 @Component
 @Service
-public class FlowTracker implements FlowTrackerService {
+public class ObjectiveTracker implements ObjectiveTrackerService {
 
     private final Logger log = getLogger(getClass());
 
@@ -110,14 +113,26 @@
         @Override
         public void run() {
             if (event.reasons() == null) {
-                delegate.bumpIntents(intentsByLink.values());
+                delegate.triggerCompile(null, false);
+
             } else {
+                Set<IntentId> toBeRecompiled = new HashSet<>();
+                boolean recompileOnly = true;
+
+                // Scan through the list of reasons and keep accruing all
+                // intents that need to be recompiled.
                 for (Event reason : event.reasons()) {
                     if (reason instanceof LinkEvent) {
                         LinkEvent linkEvent = (LinkEvent) reason;
-                        delegate.bumpIntents(intentsByLink.get(new LinkKey(linkEvent.subject())));
+                        if (linkEvent.type() == LINK_REMOVED) {
+                            Set<IntentId> intentIds = intentsByLink.get(new LinkKey(linkEvent.subject()));
+                            toBeRecompiled.addAll(intentIds);
+                        }
+                        recompileOnly = recompileOnly && linkEvent.type() == LINK_REMOVED;
                     }
                 }
+
+                delegate.triggerCompile(toBeRecompiled, !recompileOnly);
             }
         }
     }
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTrackerService.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
similarity index 96%
rename from core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTrackerService.java
rename to core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
index b96de7c..15496ff 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/FlowTrackerService.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/ObjectiveTrackerService.java
@@ -9,7 +9,7 @@
  * Auxiliary service for tracking intent path flows and for notifying the
  * intent service of environment changes via topology change delegate.
  */
-public interface FlowTrackerService {
+public interface ObjectiveTrackerService {
 
     /**
      * Sets a topology change delegate.
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
index f9cfa67..eb2e113 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -49,8 +49,8 @@
     @Override
     public void install(PathIntent intent) {
         TrafficSelector.Builder builder =
-                DefaultTrafficSelector.builder(intent.getTrafficSelector());
-        Iterator<Link> links = intent.getPath().links().iterator();
+                DefaultTrafficSelector.builder(intent.selector());
+        Iterator<Link> links = intent.path().links().iterator();
         ConnectPoint prev = links.next().dst();
 
         while (links.hasNext()) {
@@ -70,8 +70,8 @@
     @Override
     public void uninstall(PathIntent intent) {
         TrafficSelector.Builder builder =
-                DefaultTrafficSelector.builder(intent.getTrafficSelector());
-        Iterator<Link> links = intent.getPath().links().iterator();
+                DefaultTrafficSelector.builder(intent.selector());
+        Iterator<Link> links = intent.path().links().iterator();
         ConnectPoint prev = links.next().dst();
 
         while (links.hasNext()) {
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
index d8a5a95..7a9fd12 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/TopologyChangeDelegate.java
@@ -9,10 +9,14 @@
 
     /**
      * Notifies that topology has changed in such a way that the specified
-     * intents should be recompiled.
+     * intents should be recompiled. If the {@code compileAllFailed} parameter
+     * is true, the all intents in {@link org.onlab.onos.net.intent.IntentState#FAILED}
+     * state should be compiled as well.
      *
      * @param intentIds intents that should be recompiled
+     * @param compileAllFailed true implies full compile is required; false for
+     *                         selective recompile only
      */
-    void bumpIntents(Iterable<IntentId> intentIds);
+    void triggerCompile(Iterable<IntentId> intentIds, boolean compileAllFailed);
 
 }
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java
index 4143548..732d753 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java
@@ -1,12 +1,6 @@
 package org.onlab.onos.store.trivial.impl;
 
-import static org.onlab.onos.net.intent.IntentState.COMPILED;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
+import com.google.common.collect.ImmutableSet;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -21,13 +15,18 @@
 import org.onlab.onos.store.AbstractStore;
 import org.slf4j.Logger;
 
-import com.google.common.collect.ImmutableSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.onlab.onos.net.intent.IntentState.*;
+import static org.slf4j.LoggerFactory.getLogger;
 
 @Component(immediate = true)
 @Service
 public class SimpleIntentStore
-    extends AbstractStore<IntentEvent, IntentStoreDelegate>
-    implements IntentStore {
+        extends AbstractStore<IntentEvent, IntentStoreDelegate>
+        implements IntentStore {
 
     private final Logger log = getLogger(getClass());
     private final Map<IntentId, Intent> intents = new HashMap<>();
@@ -46,7 +45,7 @@
 
     @Override
     public IntentEvent createIntent(Intent intent) {
-        intents.put(intent.getId(), intent);
+        intents.put(intent.id(), intent);
         return this.setState(intent, IntentState.SUBMITTED);
     }
 
@@ -54,7 +53,7 @@
     public IntentEvent removeIntent(IntentId intentId) {
         Intent intent = intents.remove(intentId);
         installable.remove(intentId);
-        IntentEvent event = this.setState(intent, IntentState.WITHDRAWN);
+        IntentEvent event = this.setState(intent, WITHDRAWN);
         states.remove(intentId);
         return event;
     }
@@ -79,19 +78,21 @@
         return states.get(id);
     }
 
-    // TODO return dispatch event here... replace with state transition methods
     @Override
-    public IntentEvent setState(Intent intent, IntentState newState) {
-        IntentId id = intent.getId();
-        IntentState oldState = states.get(id);
-        states.put(id, newState);
-        return new IntentEvent(intent, newState, oldState, System.currentTimeMillis());
+    public IntentEvent setState(Intent intent, IntentState state) {
+        IntentId id = intent.id();
+        states.put(id, state);
+        IntentEvent.Type type = (state == SUBMITTED ? IntentEvent.Type.SUBMITTED :
+                (state == INSTALLED ? IntentEvent.Type.INSTALLED :
+                        (state == FAILED ? IntentEvent.Type.FAILED :
+                                state == WITHDRAWN ? IntentEvent.Type.WITHDRAWN :
+                                        null)));
+        return type == null ? null : new IntentEvent(type, intent);
     }
 
     @Override
-    public IntentEvent addInstallableIntents(IntentId intentId, List<InstallableIntent> result) {
+    public void addInstallableIntents(IntentId intentId, List<InstallableIntent> result) {
         installable.put(intentId, result);
-        return this.setState(intents.get(intentId), COMPILED);
     }
 
     @Override
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 270370a..b44658d 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -89,5 +89,5 @@
 }
 
 function nuke {
-    spy | cut -c7-11 | xargs kill
+    spy "$@" | cut -c7-11 | xargs kill
 }