fix: staging CI review issues (batch 1) (#883)

* fix: address staging-ci-review issues (batch 1)

- #811: Fix unreachable error handling in worker — restructure .await?
  to explicit match on nested Result so token budget errors are properly
  logged and marked as failed
- #813: Combine metadata + token budget into single update_context()
  call to prevent concurrent worker observing partial state
- #814: Persist max_tokens and total_tokens_used to both PostgreSQL and
  libSQL backends — add V12 migration, update save_job/get_job
- #815: Cap user-supplied max_tokens at configured max_tokens_per_job
  to prevent budget bypass via metadata injection
- #869: Release locks before async I/O in webhook handler (http.rs) and
  SIGHUP handler (main.rs) to prevent blocking concurrent requests

Fixes: #811, #813, #814, #815, #869

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: address PR #883 review feedback

- Fix min(user_val, 0) bug: guard for unlimited config (max_tokens_per_job == 0)
- Remove duplicate columns from libSQL base SCHEMA (v12 migration is sole source)
- Use get_i64() helper for consistency in libsql/jobs.rs
- Add regression tests for scheduler token budget capping

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Henry Park
2026-03-10 14:01:07 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 1f5b582c5f
commit 873322f2fb
7 changed files with 226 additions and 44 deletions
+12 -5
View File
@@ -151,8 +151,9 @@ impl Store {
id, conversation_id, title, description, category, status, source,
user_id,
budget_amount, budget_token, bid_amount, estimated_cost, estimated_time_secs,
actual_cost, repair_attempts, created_at, started_at, completed_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
actual_cost, repair_attempts, max_tokens, total_tokens_used,
created_at, started_at, completed_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
ON CONFLICT (id) DO UPDATE SET
title = EXCLUDED.title,
description = EXCLUDED.description,
@@ -163,6 +164,8 @@ impl Store {
estimated_time_secs = EXCLUDED.estimated_time_secs,
actual_cost = EXCLUDED.actual_cost,
repair_attempts = EXCLUDED.repair_attempts,
max_tokens = EXCLUDED.max_tokens,
total_tokens_used = EXCLUDED.total_tokens_used,
started_at = EXCLUDED.started_at,
completed_at = EXCLUDED.completed_at
"#,
@@ -182,6 +185,8 @@ impl Store {
&estimated_time_secs,
&ctx.actual_cost,
&(ctx.repair_attempts as i32),
&(ctx.max_tokens as i64),
&(ctx.total_tokens_used as i64),
&ctx.created_at,
&ctx.started_at,
&ctx.completed_at,
@@ -201,7 +206,8 @@ impl Store {
r#"
SELECT id, conversation_id, title, description, category, status, user_id,
budget_amount, budget_token, bid_amount, estimated_cost, estimated_time_secs,
actual_cost, repair_attempts, created_at, started_at, completed_at
actual_cost, repair_attempts, max_tokens, total_tokens_used,
created_at, started_at, completed_at
FROM agent_jobs WHERE id = $1
"#,
&[&id],
@@ -237,8 +243,9 @@ impl Store {
completed_at: row.get("completed_at"),
transitions: Vec::new(), // Not loaded from DB for now
metadata: serde_json::Value::Null,
total_tokens_used: 0,
max_tokens: 0,
max_tokens: row.get::<_, Option<i64>>("max_tokens").unwrap_or(0) as u64,
total_tokens_used: row.get::<_, Option<i64>>("total_tokens_used").unwrap_or(0)
as u64,
extra_env: std::sync::Arc::new(std::collections::HashMap::new()),
http_interceptor: None,
tool_output_stash: std::sync::Arc::new(tokio::sync::RwLock::new(