Skip to content

Commit 901c5a0

Browse files
committed
fix: address overseer review feedback
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 23b4406 commit 901c5a0

6 files changed

Lines changed: 148 additions & 64 deletions

File tree

Server/src/overseer/api.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ def create_overseer_app(
2323
if not api_key:
2424
raise ValueError("api_key is required")
2525
app = FastAPI(title="Overseer Control Plane", version="1.0")
26+
scoped_team_ids = (
27+
frozenset(authorized_team_ids) if authorized_team_ids is not None else None
28+
)
2629

2730
def serialize(value: object) -> object:
2831
if isinstance(value, Decimal):
@@ -36,12 +39,9 @@ def serialize(value: object) -> object:
3639
def authorize(requested_team_id: str | None, presented_key: str | None) -> None:
3740
if presented_key is None or not compare_digest(presented_key, api_key):
3841
raise HTTPException(status_code=401, detail="authentication required")
39-
if (
40-
requested_team_id is not None
41-
and authorized_team_ids is not None
42-
and requested_team_id not in authorized_team_ids
43-
):
44-
raise HTTPException(status_code=403, detail="team access denied")
42+
if scoped_team_ids is not None:
43+
if requested_team_id is None or requested_team_id not in scoped_team_ids:
44+
raise HTTPException(status_code=403, detail="team access denied")
4545

4646
@app.get("/events")
4747
def events(

Server/src/overseer/ledger.py

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from decimal import Decimal, InvalidOperation
1212
import json
1313
import sqlite3
14+
from threading import RLock
1415
from typing import Any
1516
from uuid import uuid4
1617

@@ -46,6 +47,7 @@ class EventLedger:
4647
"""SQLite-backed ledger suitable for simulation and a later API adapter."""
4748

4849
def __init__(self, connection: sqlite3.Connection):
50+
self._lock = RLock()
4951
database_path = connection.execute("PRAGMA database_list").fetchone()[2]
5052
if database_path:
5153
self._connection = sqlite3.connect(database_path, check_same_thread=False)
@@ -128,58 +130,62 @@ def record(
128130
metadata=metadata or {},
129131
created_at=normalized_created_at.isoformat(),
130132
)
131-
self._connection.execute(
132-
"""
133-
INSERT INTO ledger_events
134-
(id, category, event_type, team_id, agent_id, task_id, amount, currency,
135-
requires_approval, approved_by, metadata_json, created_at)
136-
VALUES (:id, :category, :event_type, :team_id, :agent_id, :task_id, :amount,
137-
:currency, :requires_approval, :approved_by, :metadata_json, :created_at)
138-
""",
139-
{**asdict(event), "requires_approval": int(event.requires_approval),
140-
"amount": str(event.amount),
141-
"metadata_json": json.dumps(event.metadata, sort_keys=True)},
142-
)
143-
self._connection.commit()
133+
with self._lock:
134+
self._connection.execute(
135+
"""
136+
INSERT INTO ledger_events
137+
(id, category, event_type, team_id, agent_id, task_id, amount, currency,
138+
requires_approval, approved_by, metadata_json, created_at)
139+
VALUES (:id, :category, :event_type, :team_id, :agent_id, :task_id, :amount,
140+
:currency, :requires_approval, :approved_by, :metadata_json, :created_at)
141+
""",
142+
{**asdict(event), "requires_approval": int(event.requires_approval),
143+
"amount": str(event.amount),
144+
"metadata_json": json.dumps(event.metadata, sort_keys=True)},
145+
)
146+
self._connection.commit()
144147
return event
145148

146149
def approve(self, event_id: str, approver_id: str) -> LedgerEvent:
147150
if not approver_id:
148151
raise ValueError("approver_id is required")
149-
cursor = self._connection.execute(
150-
"""
151-
UPDATE ledger_events
152-
SET requires_approval = 0, approved_by = ?
153-
WHERE id = ? AND requires_approval = 1 AND approved_by IS NULL
154-
""",
155-
(approver_id, event_id),
156-
)
157-
if cursor.rowcount != 1:
158-
raise LookupError("pending approval not found")
159-
self._connection.commit()
160-
return self.get(event_id)
152+
with self._lock:
153+
cursor = self._connection.execute(
154+
"""
155+
UPDATE ledger_events
156+
SET requires_approval = 0, approved_by = ?
157+
WHERE id = ? AND requires_approval = 1 AND approved_by IS NULL
158+
""",
159+
(approver_id, event_id),
160+
)
161+
if cursor.rowcount != 1:
162+
raise LookupError("pending approval not found")
163+
self._connection.commit()
164+
return self.get(event_id)
161165

162166
def get(self, event_id: str) -> LedgerEvent:
163-
row = self._connection.execute(
164-
"SELECT * FROM ledger_events WHERE id = ?", (event_id,)
165-
).fetchone()
167+
with self._lock:
168+
row = self._connection.execute(
169+
"SELECT * FROM ledger_events WHERE id = ?", (event_id,)
170+
).fetchone()
166171
if row is None:
167172
raise LookupError("ledger event not found")
168173
return self._row_to_event(row)
169174

170175
def list_events(self, team_id: str | None = None, limit: int | None = None) -> list[LedgerEvent]:
171176
limit_sql = "" if limit is None else " LIMIT ?"
172177
limit_params: tuple[Any, ...] = () if limit is None else (limit,)
173-
if team_id is None:
174-
rows = self._connection.execute(
175-
"SELECT * FROM ledger_events ORDER BY created_at DESC" + limit_sql,
176-
limit_params,
177-
).fetchall()
178-
else:
179-
rows = self._connection.execute(
180-
"SELECT * FROM ledger_events WHERE team_id = ? ORDER BY created_at DESC" + limit_sql,
181-
(team_id, *limit_params),
182-
).fetchall()
178+
with self._lock:
179+
if team_id is None:
180+
rows = self._connection.execute(
181+
"SELECT * FROM ledger_events ORDER BY created_at DESC" + limit_sql,
182+
limit_params,
183+
).fetchall()
184+
else:
185+
rows = self._connection.execute(
186+
"SELECT * FROM ledger_events WHERE team_id = ? ORDER BY created_at DESC" + limit_sql,
187+
(team_id, *limit_params),
188+
).fetchall()
183189
return [self._row_to_event(row) for row in rows]
184190

185191
def list_pending_approvals(
@@ -194,21 +200,23 @@ def list_pending_approvals(
194200
if limit is not None:
195201
query += " LIMIT ?"
196202
params += (limit,)
197-
rows = self._connection.execute(query, params).fetchall()
203+
with self._lock:
204+
rows = self._connection.execute(query, params).fetchall()
198205
return [self._row_to_event(row) for row in rows]
199206

200207
def summarize(self, team_id: str | None = None) -> list[TeamSummary]:
201208
where = "" if team_id is None else "WHERE team_id = ?"
202209
params: tuple[Any, ...] = () if team_id is None else (team_id,)
203-
rows = self._connection.execute(
204-
"""
205-
SELECT team_id, currency, category, requires_approval, amount
206-
FROM ledger_events
207-
""" + where + """
208-
ORDER BY team_id, currency
209-
""",
210-
params,
211-
).fetchall()
210+
with self._lock:
211+
rows = self._connection.execute(
212+
"""
213+
SELECT team_id, currency, category, requires_approval, amount
214+
FROM ledger_events
215+
""" + where + """
216+
ORDER BY team_id, currency
217+
""",
218+
params,
219+
).fetchall()
212220
totals: dict[tuple[str, str], dict[str, Any]] = {}
213221
for row in rows:
214222
key = (row["team_id"], row["currency"])

Server/src/overseer/simulation.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
import math
5+
from decimal import Decimal, InvalidOperation
66

77
from .ledger import EventLedger, LedgerEvent
88

@@ -15,9 +15,16 @@ def run_support_ticket_simulation(
1515
model_cost: float = 31.25,
1616
) -> list[LedgerEvent]:
1717
"""Record one complete support-ticket lifecycle without external side effects."""
18-
if not all(
19-
math.isfinite(float(amount)) and amount >= 0
20-
for amount in (subscription_amount, model_cost)
18+
try:
19+
exact_subscription_amount = Decimal(str(subscription_amount))
20+
exact_model_cost = Decimal(str(model_cost))
21+
except (InvalidOperation, TypeError, ValueError):
22+
raise ValueError(
23+
"subscription_amount and model_cost must be finite and non-negative"
24+
) from None
25+
if any(
26+
not amount.is_finite() or amount < 0
27+
for amount in (exact_subscription_amount, exact_model_cost)
2128
):
2229
raise ValueError("subscription_amount and model_cost must be finite and non-negative")
2330
task_id = f"ticket:{ticket_id}"
@@ -44,7 +51,7 @@ def run_support_ticket_simulation(
4451
team_id="support",
4552
agent_id="support-agent",
4653
task_id=task_id,
47-
amount=model_cost,
54+
amount=exact_model_cost,
4855
currency="USD",
4956
),
5057
ledger.record(
@@ -53,7 +60,7 @@ def run_support_ticket_simulation(
5360
team_id="support",
5461
agent_id="billing-agent",
5562
task_id=task_id,
56-
amount=subscription_amount,
63+
amount=exact_subscription_amount,
5764
currency="USD",
5865
),
5966
]

Server/tests/test_overseer_api.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
11
import sqlite3
22

3+
import pytest
34
from fastapi.testclient import TestClient
45

56
from overseer import EventLedger, create_overseer_app, run_support_ticket_simulation
67

78

9+
@pytest.mark.parametrize(
10+
("subscription_amount", "model_cost"),
11+
[(float("nan"), 31.25), (499, float("inf")), (-1, 31.25), (499, -1)],
12+
)
13+
def test_simulation_validates_amounts_before_recording_events(
14+
subscription_amount, model_cost
15+
):
16+
ledger = EventLedger(sqlite3.connect(":memory:"))
17+
18+
with pytest.raises(ValueError, match="finite and non-negative"):
19+
run_support_ticket_simulation(
20+
ledger,
21+
subscription_amount=subscription_amount,
22+
model_cost=model_cost,
23+
)
24+
25+
assert ledger.list_events() == []
26+
27+
828
def test_api_exposes_simulation_events_and_financial_summary():
929
ledger = EventLedger(sqlite3.connect(":memory:"))
1030
run_support_ticket_simulation(ledger, ticket_id="T-42")
@@ -63,10 +83,23 @@ def test_api_requires_authentication_and_enforces_team_scope():
6383
)
6484

6585
assert client.get("/events").status_code == 401
86+
assert (
87+
client.get(
88+
"/events",
89+
headers={"X-Overseer-Api-Key": "test-key"},
90+
).status_code
91+
== 403
92+
)
6693
assert (
6794
client.get(
6895
"/events?team_id=lead-generation",
6996
headers={"X-Overseer-Api-Key": "test-key"},
7097
).status_code
7198
== 403
7299
)
100+
101+
for path in ("/summaries", "/approvals"):
102+
assert (
103+
client.get(path, headers={"X-Overseer-Api-Key": "test-key"}).status_code
104+
== 403
105+
)

Server/tests/test_overseer_ledger.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sqlite3
2+
from concurrent.futures import ThreadPoolExecutor
23
from datetime import datetime, timezone
34
from decimal import Decimal
45

@@ -78,10 +79,19 @@ def test_exact_money_is_currency_aware_and_pending_amounts_are_excluded(ledger):
7879
assert by_currency["EUR"].revenue == Decimal("0")
7980
assert by_currency["EUR"].pending_approvals == 1
8081

82+
ledger.approve(
83+
ledger.list_pending_approvals()[0].id,
84+
"operator-1",
85+
)
86+
by_currency = {summary.currency: summary for summary in ledger.summarize()}
87+
assert by_currency["EUR"].revenue == Decimal("100")
88+
8189

8290
def test_rejects_non_finite_or_naive_timestamps(ledger):
8391
with pytest.raises(ValueError, match="finite"):
8492
ledger.record(category="cost", event_type="x", team_id="t", agent_id="a", amount=float("nan"))
93+
with pytest.raises(ValueError, match="finite"):
94+
ledger.record(category="cost", event_type="x", team_id="t", agent_id="a", amount=float("inf"))
8595
with pytest.raises(ValueError, match="timezone-aware"):
8696
ledger.record(
8797
category="activity",
@@ -100,6 +110,15 @@ def test_rejects_non_finite_or_naive_timestamps(ledger):
100110
)
101111
assert event.created_at.endswith("+00:00")
102112

113+
offset_event = ledger.record(
114+
category="activity",
115+
event_type="x",
116+
team_id="t",
117+
agent_id="a",
118+
created_at=datetime.fromisoformat("2025-01-01T01:00:00+01:00"),
119+
)
120+
assert offset_event.created_at == "2025-01-01T00:00:00+00:00"
121+
103122

104123
def test_requires_approval_is_visible_and_can_be_approved(ledger):
105124
event = ledger.record(
@@ -143,3 +162,18 @@ def test_rejects_unsafe_or_ambiguous_events(ledger):
143162
requires_approval=True,
144163
approved_by="operator",
145164
)
165+
166+
167+
def test_serializes_concurrent_worker_thread_access(ledger):
168+
def record_event(index):
169+
ledger.record(
170+
category="activity",
171+
event_type="worker_event",
172+
team_id="support",
173+
agent_id=f"agent-{index}",
174+
)
175+
176+
with ThreadPoolExecutor(max_workers=8) as executor:
177+
list(executor.map(record_event, range(40)))
178+
179+
assert len(ledger.list_events(team_id="support")) == 40

docs/overseer-control-plane.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@ app = create_overseer_app(ledger, api_key="set-this-from-secret-storage")
5656

5757
It provides authenticated `GET /events`, `GET /summaries`, and `GET /approvals`
5858
using the `X-Overseer-Api-Key` header. Pass `authorized_team_ids` when the
59-
operator should be restricted to a subset of teams. The
59+
operator should be restricted to a subset of teams; scoped requests must
60+
include a team ID from that set. The
6061
`run_support_ticket_simulation()` helper can populate a local demo ledger so
6162
the dashboard can be built and reviewed before any provider credentials exist.
62-
When the API is used, create the SQLite connection with
63-
`EventLedger` will copy it to a thread-safe connection for API handlers. File-
63+
When the API is used, `EventLedger` copies the SQLite connection to one that is
64+
safe for API handlers. File-
6465
backed databases remain persistent, while in-memory databases are copied into
65-
an isolated thread-safe database owned by the ledger.
66+
an isolated database owned by the ledger, with access serialized for FastAPI
67+
worker threads.
6668

6769
Amounts are stored as exact decimals and summaries are separated by currency.
6870
Pending approval events remain visible in the activity and approval feeds but

0 commit comments

Comments
 (0)