Skip to content

Commit b800a9c

Browse files
committed
fix(observatory): clamp held_seconds and treat epoch-0 claim as present
Fold gitar edge-case: use 'claimed_at is not None' so an epoch-0 timestamp is not read as missing, and max(0, ...) so clock skew (a claim stamped slightly in the future) cannot report a negative age. Adds a clock-skew test.
1 parent 8859d4a commit b800a9c

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

tests/test_routes_observatory.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,24 @@ async def test_fleet_idle_agent_has_uniform_badge_shape(app, client):
211211
assert len(idle) == 1
212212
assert idle[0]["held_seconds"] is None
213213
assert idle[0]["stale"] is False
214+
215+
216+
@pytest.mark.asyncio
217+
async def test_fleet_clamps_held_seconds_under_clock_skew(app, client):
218+
import time
219+
220+
pstore = app.state.project_store
221+
tstore = app.state.project_task_store
222+
proj = await pstore.create_project(name="ObsSkew", slug="obs-skew", created_by="admin", user_id="admin")
223+
task = await tstore.create_task(proj["id"], title="Future card", created_by="admin")
224+
await tstore.claim_task(task["id"], "@lane-skew")
225+
# Claim stamped slightly in the future (clock skew) must not yield a negative age.
226+
await tstore._db.execute(
227+
"UPDATE project_tasks SET claimed_at = ? WHERE id = ?", (time.time() + 600, task["id"]))
228+
await tstore._db.commit()
229+
230+
agents = (await client.get("/api/observatory/fleet")).json()["agents"]
231+
mine = [a for a in agents if a["handle"] == "@lane-skew"]
232+
assert len(mine) == 1
233+
assert mine[0]["held_seconds"] == 0
234+
assert mine[0]["stale"] is False

tinyagentos/routes/observatory.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,13 @@ async def get_fleet(request: Request, user: CurrentUser = Depends(current_user))
210210
continue
211211
working.add(handle)
212212
# Claim age drives the stale badge. claimed_at is a Unix epoch set on
213-
# claim; guard a missing/None value (it must not break the view).
213+
# claim. Use `is not None` so an epoch-0 timestamp is not mistaken for
214+
# missing, and clamp to >= 0 so clock skew (claimed_at slightly in the
215+
# future) cannot report a negative age.
214216
claimed_at = t.get("claimed_at")
215-
held_seconds = int(now - claimed_at) if claimed_at else None
217+
held_seconds = (
218+
max(0, int(now - claimed_at)) if claimed_at is not None else None
219+
)
216220
agents.append({
217221
"handle": handle,
218222
"state": "working",

0 commit comments

Comments
 (0)