anchor.diff:    end.
   
   handle_response(State, {ok, {{_HttpVersion, 200, _ReasonPhrase}, _Headers, Body}}) ->
  -  NewState = parse_json_to_vips(State, Body),
  +  Now = erlang:monotonic_time(seconds),
  +  NewState = parse_json_to_vips(State, Body, Now),
     {ok, NewState};
   handle_response(_State, Response) ->
     lager:debug("Bad HTTP Response: ~p", [Response]),
     {error, http_error}.
   
  -prune_old_agents(AgentCache) ->
  -  map:filter(fun (#agent_cache{last_seen_time = LastSeenTime}) ->
  -                   Now = erlang:monotonic_time(seconds),
  -                   Now - LastSeenTime =< minuteman_config:agent_reregistration_threshold()
  -               end, AgentCache).
  -
  -%% TODO(tyler) break up this long horrible function
  -%% TODO(tyler) test agent_cache
  --spec(parse_json_to_vips(State :: #state{}, Data :: binary()) -> #state{}).
  -parse_json_to_vips(State = #state{last_master_elected = LastMaster,
  -                                  last_master_elected_local_time = LastMasterTime,
  -                                  agent_cache = AgentCache},
  -                   Data) ->
  +-spec(filter_old_agents(State :: #state{}, ElectedTime :: float(), Now :: integer()) -> #state{}).
  +filter_old_agents(State = #state{last_master_elected = LastMaster,
  +                                 last_master_elected_local_time = LastMasterTime,
  +                                 agent_cache = AgentCache},
  +                 ElectedTime, Now) ->
  +  ReregistrationThreshold = minuteman_config:agent_reregistration_threshold(),
  +  LastMasterCanKillOldAgents = Now - LastMasterTime > ReregistrationThreshold,
  +  case {ElectedTime == LastMaster, LastMasterCanKillOldAgents} of
  +    {false, _} ->
  +      State#state{last_master_elected = ElectedTime,
  +                  last_master_elected_local_time = Now};
  +    {true, true} ->
  +      %% This master is old enough to have invalidated any tasks on
  +      %% slaves that have not yet reconnected.
  +      FilterFunc = fun (#agent_cache{last_seen_time = LastSeenTime}) ->
  +                       Now - LastSeenTime =< ReregistrationThreshold
  +                   end,
  +      PrunedCache = map:filter(FilterFunc, AgentCache),
  +      State#state{agent_cache = PrunedCache};
  +    {true, _} ->
  +      %% This master is not old enough to trust with agent evictions
  +      State
  +  end.
  +
  +-spec(parse_json(Data :: binary()) -> {float(), list(map()), list(map())}).
  +parse_json(Data) ->
     Parsed = jsx:decode(Data, [return_maps, {labels, atom}]),
     ElectedTime = maps:get(elected_time, Parsed),
  -  Now = erlang:monotonic_time(seconds),
  -
  -  LastMasterCanKillOldAgents = Now - LastMasterTime > minuteman_config:agent_reregistration_threshold(),
  -  State2 = case {ElectedTime == LastMaster, LastMasterCanKillOldAgents} of
  -             {false, _} ->
  -               State#state{last_master_elected = ElectedTime,
  -                           last_master_elected_local_time = Now};
  -             {true, true} ->
  -               %% This master is old enough to have invalidated any tasks on
  -               %% slaves that have not yet reconnected.
  -               PrunedCache = prune_old_agents(AgentCache),
  -               State#state{agent_cache = PrunedCache};
  -             {true, _} ->
  -               %% This master is not old enough to trust with agent evictions
  -               State
  -           end,
  -
  +  Agents = maps:get(slaves, Parsed),
     Frameworks = maps:get(frameworks, Parsed),
  -  Agents = maps:get(slaves, Parsed),
  -  FrameworkTasks = frameworks_to_tasks(Frameworks),
  -
  -  RawActiveAgents = lists:filter(fun(Agent) ->
  -                                     maps:get(active, Agent)
  -                                 end, Agents),
  +  {ElectedTime, Agents, Frameworks}.
  +
  +%% TODO(tyler) test agent_cache
  +-spec(parse_json_to_vips(State :: #state{}, Data :: binary(), Now :: integer()) -> #state{}).
  +parse_json_to_vips(State, Data, Now) ->
  +  {ElectedTime, Agents, Frameworks} = parse_json(Data),
  +
  +  %% Filter agents if we have seen this master for long enough for it
  +  %% to have marked an agent as being down.
  +  State2 = filter_old_agents(State, ElectedTime, Now),
  +
  +  FrameworkTasks = lists:flatmap(fun (#{tasks := Tasks}) -> Tasks end, Frameworks),
  +  RawActiveAgents = lists:filter(fun(Agent) -> maps:get(active, Agent) end, Agents),
   
     %% Get a map from agent ID to #agent_cache{}, we will trust these active agents
  -  ActiveAgents = lists:foldl(fun(#{pid := Pid, id := Id}, AccIn) ->
  -                                 AgentTasks = lists:filter(fun (Task) ->
  -                                                               TaskAgent = maps:get(slave_id, Task),
  -                                                               TaskAgent == Id
  -                                                           end, FrameworkTasks),
  -                                 IP = libprocess_pid_to_ip(Pid),
  -                                 AccIn#{Id => #agent_cache{last_seen_time = Now,
  -                                                           last_seen_tasks = AgentTasks,
  -                                                           last_seen_ips = IP}}
  -                             end, #{}, RawActiveAgents),
  -
  -  %% Merge cached tasks with tasks from active agents
  +  ActiveAgents = agents_to_agent_caches(RawActiveAgents, FrameworkTasks, Now),
  +
  +  %% Merge the detected active agents with the cached, possibly-wandering ones.
     CachedAgents = State2#state.agent_cache,
     MergedAgentCache = maps:merge(CachedAgents, ActiveAgents),
     State3 = State2#state{agent_cache = MergedAgentCache},
  
positive.code:     }
    ],
    ?assertEqual(Expected, Vips).
  bad_state_test() ->
    {ok, Data} = file:read_file("testdata/bad-state-gaal.json"),
    #state{vips = Vips} = parse_json_to_vips(#state{}, Data),
    Expected = [
      {
        {tcp, {10, 22, 126, 49}, 5000},
  
negative.code:                           end, [], MergedAgentCache),
  
    AgentIPs = get_agent_ips(MergedAgentCache),
    FoldFun = task_fold_fun(AgentIPs),
    Vips = lists:foldl(FoldFun, orddict:new(), MergedTasks),
  
    State3#state{vips = Vips}.
  
  agents_to_agent_caches(RawActiveAgents, FrameworkTasks, Now) ->
    FoldFunc = fun(#{pid := Pid, id := Id}, AccIn) ->
                   AgentTasks = lists:filter(fun (Task) ->
                                                 TaskAgent = maps:get(slave_id, Task),
                                                 TaskAgent == Id
                                             end, FrameworkTasks),
                   IP = libprocess_pid_to_ip(Pid),
                   AccIn#{Id => #agent_cache{last_seen_time = Now,
                                             last_seen_tasks = AgentTasks,
                                             last_seen_ips = IP}}
               end,
  
    lists:foldl(FoldFunc, #{}, RawActiveAgents).
  
  
  libprocess_pid_to_ip(LibprocessPid) when is_binary(LibprocessPid) ->
  