Office Hours — How do you build a time-based AI coding agent that can reason about deadlines and scheduling constraints? A daily developer question about AI/LLMs, answered with a direct, opinionated take. 2026-09-08T12:00:00.000Z Office Hours Office Hours office-hoursq-and-apractical-ai

Office Hours — How do you build a time-based AI coding agent that can reason about deadlines and scheduling constraints?

A daily developer question about AI/LLMs, answered with a direct, opinionated take.

Daily One question from the trenches, one opinionated answer.

How do you build a time-based AI coding agent that can reason about deadlines and scheduling constraints?

The honest answer: most teams don’t. They build agents that chase objectives, not agents that reason about when to finish. The difference matters because a deadline-aware agent is fundamentally different from a task-completion agent, and building one requires explicit architecture choices that most codebases skip.

The Core Problem

Time is not a first-class citizen in how LLMs reason. A frontier model like Claude Opus 5 or GPT-5.6 Sol can parse a deadline from text and acknowledge it in a response, but it won’t naturally trade off task completeness against elapsed time. Give it a 12-hour deadline to refactor a codebase, and it will try to make changes perfect rather than good-enough-by-deadline. You end up with an agent that either ships incomplete work or ignores the constraint entirely.

This happens because LLMs optimize for task completion signal (test passes, goal reached), not for constrained optimization (goal reached within time bound). During training, there’s no penalty for taking 20 steps when 5 would suffice, as long as the answer is right.

Architecture: Time as Explicit State

Build a three-layer structure that separates planning from execution:

  1. Time budget allocation layer. Before the agent starts work, compute how much time remains and divide it among subtasks. This isn’t magic, it’s just math. If you have 4 hours and 3 tasks, assign ~1.3 hours per task with a 10-minute buffer. Model can participate in this allocation, but the budget itself must be explicit state, not implicit reasoning.

  2. Per-step wall-clock enforcement. Every function call to the agent includes a hard deadline for response (e.g., “respond by 14:30 UTC”). Use a simple check before returning: if current time exceeds deadline, truncate work and return best-effort result, don’t try to squeeze in one more step.

  3. Quality-time trade-off mechanism. Define what “good enough” means at each time threshold. At 80% of budget remaining, agent can iterate. At 20% remaining, agent should commit to a solution. This requires explicit thresholds in your prompt, not vague instructions.

Here’s a concrete pattern:

import time
from dataclasses import dataclass

@dataclass
class TimeContext:
    start_time: float
    deadline: float
    total_budget_seconds: float
    
    @property
    def elapsed(self) -> float:
        return time.time() - self.start_time
    
    @property
    def remaining(self) -> float:
        return max(0, self.deadline - time.time())
    
    @property
    def percent_complete(self) -> float:
        return min(1.0, self.elapsed / self.total_budget_seconds)
    
    def should_finalize(self) -> bool:
        # Hard cutoff: if 75% of time spent, start wrapping up
        return self.percent_complete > 0.75

def agent_step(task: str, time_context: TimeContext) -> str:
    if time_context.remaining < 30:  # Hard stop with 30s buffer
        return f"TIME UP. Returning best-effort result: {current_best_result}"
    
    quality_mode = "iterate" if time_context.percent_complete < 0.6 else "finalize"
    
    prompt = f"""
Task: {task}
Quality mode: {quality_mode}
Time remaining: {time_context.remaining:.0f}s
Budget: {time_context.total_budget_seconds:.0f}s

If quality_mode is 'iterate', refine your approach.
If quality_mode is 'finalize', commit to a solution and stop iterating.
"""
    
    response = model.generate(prompt)
    return response

The key insight: don’t ask the model “how much time do you need?” Ask it to work within constraints you define. Constraints are cheap. Open-ended optimization is expensive.

The Scheduling Constraint Problem

Deadlines often interact with resource constraints (only one agent can use the database at 3pm, only two team members available tomorrow). You need a constraint store that the agent can query but not rewrite:

class SchedulingConstraints:
    def __init__(self):
        self.unavailable_windows = {
            "database_write": [("15:00", "15:30")],
            "team_review": [("08:00", "09:00")],
        }
    
    def can_proceed(self, resource: str, current_time: str) -> bool:
        windows = self.unavailable_windows.get(resource, [])
        return not any(start <= current_time <= end for start, end in windows)
    
    def next_available(self, resource: str, current_time: str) -> str:
        # Return the soonest time this resource is free
        windows = self.unavailable_windows.get(resource, [])
        for start, end in sorted(windows):
            if current_time < start:
                return end
        return current_time

Expose this as a tool the agent can call. “When can I run the test suite?” beats guessing. The agent can then reason about waiting versus parallelizing other work.

The Real Bottleneck: What “Done” Means

Most deadline failures happen because “done” is ambiguous. If your agent’s goal is “refactor the authentication module,” it doesn’t know if that means:

  • All tests passing (hard stop: pass/fail)
  • Code review approval (soft stop: review takes 30min)
  • Deployment to staging (hard dependency: merge blocks deployment)
  • Monitoring dashboards green (time-dependent: takes minutes to stabilize)

Make this explicit before the agent starts. Frame tasks with concrete exit criteria tied to time:

task_spec = {
    "objective": "refactor auth module",
    "success_criteria": [
        ("unit_tests_pass", "hard", 0),  # Must pass immediately
        ("integration_tests_pass", "hard", 0),
        ("code_review", "soft", 1800),  # Soft: expect ~30min
        ("staging_deploy", "soft", 3600),  # Soft: expect ~1hour
    ],
    "deadline_unix": int(time.time()) + 14400,  # 4 hours from now
}

Then in your agent loop, at each decision point, calculate: “If I stop now, can the remaining success criteria complete before deadline?” That’s your signal to finalize.

Multi-Agent Scheduling

If you’re running multiple agents and they depend on each other (one does code generation, another does review, another deploys), you must serialize their work and allocate time upfront. Deadlock is easy. One agent waiting for another wastes wall-clock time that can’t be recovered.

Use a simple queue with explicit time slots:

queue = [
    {"agent": "codegen", "start": "14:00", "duration_minutes": 90},
    {"agent": "reviewer", "start": "15:30", "duration_minutes": 30},
    {"agent": "deployer", "start": "16:00", "duration_minutes": 30},
]

Each agent knows its time slot before work begins. No surprise contention, no surprise delays.

The Observability Piece

Log every time decision. When the agent finalizes early or hits a deadline wall, you need to know:

  • How many steps it completed versus planned
  • Where it cut corners (which tests were skipped, which optimizations deferred)
  • Whether the deadline was too tight or the task was underestimated

This feedback loop is how you get better at allocating time next time. Without it, deadlines become cargo-cult constraints that don’t improve agent behavior.

Bottom line: Don’t rely on the model to reason about time management on its own. Build explicit time budgets, quality-mode thresholds, and constraint stores into your agent’s execution loop. Treat scheduling like a resource constraint (CPU, memory) rather than a soft heuristic the model should optimize.

Question via Hacker News