Inspiration

Recruiting teams spend a surprising amount of time on coordination instead of judgment: chasing interview feedback, following up with candidates, checking whether a panel is ready to schedule, and figuring out which workflows actually need attention.

We wanted to explore a simple question: What if an AI agent could handle the operational work around recruiting without ever being allowed to make the hiring decision itself?

That became CandidateLoop.

Our core principle is simple:

Automate coordination, not hiring judgment.

CandidateLoop independently handles routine recruiting operations, but when a workflow reaches a decision that requires human judgment, the agent stops, escalates it to a recruiter, and waits. Once the recruiter makes the decision, CandidateLoop resumes the operational work from there.

What it does

CandidateLoop is an autonomous recruiting operations agent that reviews active candidate workflows and determines what operational action, if any, should happen next.

It can:

  • Send reminders when required interview feedback is missing
  • Send neutral candidate status updates when communication is overdue
  • Detect when interview evidence is complete and create a human decision request
  • Schedule the next interview only after a recruiter explicitly advances a candidate
  • Record a no-action outcome when a workflow is already up to date

CandidateLoop is intentionally prevented from autonomously advancing, holding, rejecting, hiring, ranking, or scoring candidates.

A typical workflow looks like this:

  1. A candidate completes an interview, but one required scorecard is missing.
  2. CandidateLoop detects the missing feedback and sends a reminder.
  3. Once all required feedback is complete, CandidateLoop recognizes that the next step requires human judgment and pauses the workflow.
  4. The recruiter receives a Human Decision Required request and chooses Advance, Hold, or Reject.
  5. If the recruiter chooses Advance, the candidate moves into the scheduling stage, but CandidateLoop does not immediately schedule anything.
  6. On the next agent run, CandidateLoop sees the recruiter-approved state and schedules the next interview.
  7. On subsequent runs, CandidateLoop recognizes that the workflow is already current and takes no duplicate action.

This separation between agent execution and human judgment is the core of CandidateLoop.

How we built it

CandidateLoop consists of three main layers: a recruiting operations interface, an agent backend, and a constrained agent execution layer.

Frontend

The frontend is a dark recruiting operations workspace built with React and Vinext and deployed using Cloudflare Workers.

Instead of building a generic chatbot, we designed the interface around three concepts:

  • Dashboard: what is happening
  • CandidateLoop Agent: what the agent can handle, what it did, and why
  • Human Decisions: what only the recruiter can decide

The workspace includes candidate workflow stages, agent activity, human decision requests, operational audit information, and a dedicated Agent Console.

During a live agent run, the interface only displays information the system actually knows. It does not fabricate token streaming, per-candidate completion, or hidden model reasoning.

Agent backend

The backend is a Dockerized FastAPI service running on a ZimaOS homelab.

It manages candidate state, interview feedback, agent actions, recruiter decisions, scheduling state, and the agent execution API.

The backend is packaged as a Docker image and runs independently from the frontend. A Cloudflare Tunnel securely exposes the homelab service to the deployed frontend without directly exposing the machine to the public internet.

Strands Agents SDK

For the autonomous agent layer, we use the Strands Agents SDK.

Strands connects the language model to a deliberately constrained set of recruiting tools, including:

  • get_active_candidates
  • get_candidate
  • get_interview_feedback
  • get_interviewer_availability
  • send_feedback_reminder
  • send_candidate_status_update
  • create_human_decision
  • schedule_interview
  • record_no_action

Just as importantly, CandidateLoop deliberately does not expose tools for:

  • Advancing candidates
  • Holding candidates
  • Rejecting candidates
  • Hiring candidates
  • Ranking candidates
  • Scoring candidates
  • Resolving hiring decisions

This means the human decision boundary is not simply a prompt telling the model to behave. The agent literally does not have tools that allow it to make those decisions.

Amazon Nova and OpenRouter

CandidateLoop uses Amazon Nova 2 Lite as the model powering the Strands agent.

Our original architecture was designed to invoke Nova directly through Amazon Bedrock. During development, however, our AWS account encountered a Bedrock model-access restriction that we could not resolve within the hackathon timeline.

Rather than redesigning the agent, we kept Strands and Amazon Nova in place and routed Nova through OpenRouter while the direct Bedrock access issue was under review.

This allowed the agent architecture, tool boundaries, policies, and model behavior to remain the same while changing only the model transport.

Safety and reliability

A major focus of CandidateLoop was making agent behavior predictable, constrained, and auditable.

Human decisions are structurally protected

CandidateLoop follows the rule:

Automate coordination, not hiring judgment.

When interview evidence is complete, the agent cannot advance or reject the candidate. Instead, it creates a human decision request and stops.

The recruiter must explicitly choose Advance, Hold, or Reject.

Even after the recruiter selects Advance, CandidateLoop does not immediately schedule the next interview. The recruiter decision changes the workflow state first. On a subsequent agent run, CandidateLoop sees that explicit human approval and resumes coordination.

Scheduling requires prior human approval

Scheduling is protected by backend policy.

For CandidateLoop to schedule the next interview, the candidate must be in the appropriate scheduling stage and the most recent human resolution must explicitly be ADVANCE.

This means we do not rely solely on the language model to follow instructions. The application itself enforces the rule.

Exactly one outcome per candidate

Each active candidate receives exactly one terminal workflow outcome during an agent run.

That outcome may be:

  • Feedback reminder
  • Candidate status update
  • Human decision request
  • Interview scheduling
  • No action

The system prevents competing or duplicate terminal actions for the same candidate during a single run.

Failure rollback

Before live Strands execution, CandidateLoop snapshots its application state.

If model execution, configuration, or the agent runtime fails, CandidateLoop restores the previous state and surfaces the failure rather than silently continuing with partially completed work.

Auditable explanations

CandidateLoop exposes operational audit information using four fields:

Observed → Reason → Action → Result

These explanations show why an operational action occurred without exposing private model chain-of-thought.

Challenges we faced

One of our biggest challenges was determining the right boundary between autonomy and human control.

It would have been easy to build an agent that recommends or executes every next step. Instead, we treated hiring judgment as a hard architectural boundary and designed the workflow around the agent stopping and resuming.

Infrastructure created another challenge. We originally planned to invoke Amazon Nova directly through Bedrock, but our AWS account encountered a model-access restriction during development. We adapted by routing Nova through OpenRouter while preserving Strands, our agent architecture, and all of our safety boundaries.

We also had to think carefully about truthful agent UX.

Live model runs typically take around 15–20 seconds, but our backend does not stream reliable per-candidate completion events. Rather than displaying fake progress or simulated model reasoning, CandidateLoop shows only what is actually known while the run is executing and displays candidate outcomes only after the backend returns them.

Finally, extensive responsive testing uncovered a layout issue where the Human Decisions panel could overlap Agent Activity at smaller viewport widths. We traced the problem to a percentage-height interaction in the responsive grid, corrected the layout using breakpoint-scoped flex sizing, and verified the interface across desktop, tablet, and mobile widths.

What we learned

The biggest lesson from CandidateLoop was that building a useful autonomous agent is not primarily about giving a language model more freedom.

It is about designing the right tools, policies, state transitions, and boundaries around the model.

Strands made it possible to connect model reasoning to real application actions, but one of our most important design decisions was determining which tools the agent should never receive.

We also learned that human-in-the-loop systems become much stronger when human approval is represented as actual application state, rather than simply adding a confirmation dialog.

In CandidateLoop, recruiter approval changes the candidate workflow state. The agent then observes that state during a later execution and determines whether additional coordination is permitted.

That makes the human boundary observable, testable, and auditable.

What's next

The current hackathon version uses an in-memory repository and simulated recruiting integrations. The next step would be connecting CandidateLoop to persistent storage and real recruiting infrastructure such as an ATS, email, calendars, and interview-feedback systems.

We would also move the backend from the hackathon homelab/tunnel deployment to stable cloud infrastructure and connect directly to Amazon Bedrock once account access is available.

Longer term, we see CandidateLoop as an agentic operations layer around an ATS.

It should not decide who gets hired.

Instead, it should make sure candidates, recruiters, and interviewers are never stuck waiting on operational work that software can safely handle.

CandidateLoop keeps the process moving while keeping the decision human.

Built With

Share this project:

Updates