AI progress: honest milestone bar from closed JSON contract units

Replace the char-count estimate (EST_OUTPUT_CHARS) with ContractProgress:
points are earned only when real output units complete — first token (5),
each closed contract key / array item (5-90), root closed (95), stored (100).
Reasoning/thinking streams no longer fake progress: the first thinking
delta fires the first-token milestone ('the model is alive') and holds.
Surface the job's tone through ProcessingJobOut/JobInfo so the bar can
label itself 'Summarizing dry wit…'. Test rewritten to pin the new
honest-thinking semantics.
This commit is contained in:
avi 2026-09-18 15:00:04 -05:00
commit 61f2124b1c
6 changed files with 197 additions and 42 deletions

View file

@ -57,6 +57,8 @@ data class JobInfo(
/** Display-only phase ("loading-model", "transcribing") + 0-100 progress. */
val stage: String? = null,
val progress: Int? = null,
/** Voice a running summarize job writes in ("dry, witty"); labels the bar. */
val tone: String? = null,
/** ISO-8601 UTC when the job started running (null if unknown). */
val startedAt: String? = null,
/** ISO-8601 UTC when the job was queued (null if unknown). The wait
@ -137,6 +139,7 @@ fun parseJobs(raw: String): List<JobInfo> = runCatching {
error = o.optString("error", null).takeUnless { it.isNullOrBlank() },
stage = o.optString("stage", null).takeUnless { it.isNullOrBlank() },
progress = if (o.has("progress") && !o.isNull("progress")) o.optInt("progress") else null,
tone = o.optString("tone", null).takeUnless { it.isNullOrBlank() },
startedAt = o.optString("started_at", null).takeUnless { it.isNullOrBlank() },
createdAt = o.optString("created_at", null).takeUnless { it.isNullOrBlank() },
)