Replace the footer's placeholder usage with a real observation ledger
The footer showed invented percentages. It now shows what two harnesses actually report, or says it does not know. lumbridge-core gains an append-only per-profile UsageLedger and a projection that labels every derived value estimated, withholds a burn rate from a single sample, withholds a window fraction with no reported ceiling, withholds an exhaustion estimate that lands after the reset, and reports an expired window as rolled over rather than freezing its last percentage. A missing fact renders as missing, never as zero. (0012) lumbridge-harness is the impure side: processes, clocks, and untrusted wire text in, observations out. Three adapters: - Codex's account/rateLimits/read over the app-server's JSON-RPC stdio. The client cannot express a request outside a two-variant enum and answers every server-to-client request with -32601, so a harness asking Lumbridge for a credential is refused by construction. (0013) - Claude Code's session transcripts, as a byte-offset tail follower that reports nothing until the backlog is read to EOF — a partially-read backlog is indistinguishable from a burst of spend, and the first run against 20 MB reported forty-six billion tokens an hour. The parser models four counters, so the conversations in those files are not representable. (0014) - Claude Code's five-hour and seven-day subscription windows, via a bridge installed as its statusLine command. 0014 had claimed no such surface existed; it does, and the record is corrected in place rather than quietly edited. Lumbridge does not read the OAuth credential to call the account usage endpoint, which is what comparable tools do — AGENTS.md forbids it, and 0015 says so rather than leaving the gap unexplained. Also in here: a capability-check ordering fix in the workspace reducer, where the applied-request replay table was consulted before the capability check and so answered questions the caller had no right to ask; the GPUI spike wired to the live probes with per-harness gauges and provenance chips; and a launcher that matches its own window by PID, because GPUI sets WM_NAME but not _NET_WM_NAME and a title match never succeeded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7fe84f71e2
commit
ef52aa7ce2
@@ -0,0 +1,68 @@
|
||||
//! Documented status and usage probes for supervised coding harnesses.
|
||||
//!
|
||||
//! `lumbridge-core` owns the usage ledger and stays free of IO, clocks, and
|
||||
//! async. This crate is the other side of that boundary: it runs processes,
|
||||
//! reads a clock, parses untrusted wire text, and hands back
|
||||
//! [`lumbridge_core::UsageObservation`] values. Nothing here interprets a
|
||||
//! usage number; it only decides which observation is honest to record.
|
||||
//!
|
||||
//! The rule every probe follows: Lumbridge observes documented surfaces of a
|
||||
//! harness it launched, and never reads that harness's credentials. A probe
|
||||
//! that cannot obtain a reading records
|
||||
//! [`lumbridge_core::UsageObservation::unavailable`] rather than a zero.
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod child;
|
||||
pub mod claude;
|
||||
mod clock;
|
||||
pub mod codex;
|
||||
mod jsonrpc;
|
||||
mod probe;
|
||||
|
||||
pub use child::PROBE_ENV_ALLOWLIST;
|
||||
pub use clock::MonotonicWallClock;
|
||||
pub use jsonrpc::ServerRequestClass;
|
||||
pub use probe::{ProbeHealth, ProbeOutcome, UsageProbe};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Failures a probe can report.
|
||||
///
|
||||
/// Every variant is [`Copy`] and carries only a static discriminator, an
|
||||
/// [`std::io::ErrorKind`], or a number. No variant can capture a string from a
|
||||
/// child process or the filesystem, so an error can never smuggle a path,
|
||||
/// a credential, or attacker-chosen text into a log or a UI surface.
|
||||
#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
|
||||
pub enum HarnessError {
|
||||
#[error("a probe program name must not be empty")]
|
||||
EmptyProgram,
|
||||
#[error("the environment variable {0} is not on the probe allowlist")]
|
||||
EnvNotAllowlisted(&'static str),
|
||||
#[error("a probe poll interval must be at least one second")]
|
||||
ProbeIntervalTooShort,
|
||||
#[error("the probe program could not be started ({0:?})")]
|
||||
SpawnFailed(std::io::ErrorKind),
|
||||
#[error("the probe program closed its output")]
|
||||
Eof,
|
||||
#[error("the probe program sent a {0} byte line, over the accepted limit")]
|
||||
OversizedLine(usize),
|
||||
#[error("the probe program sent a line that is not a JSON-RPC frame")]
|
||||
Malformed,
|
||||
#[error("the harness rejected the request with JSON-RPC code {0}")]
|
||||
Rejected(i64),
|
||||
#[error("the harness has no subscription account to report a quota for")]
|
||||
NotAuthenticated,
|
||||
}
|
||||
|
||||
impl HarnessError {
|
||||
/// Whether this failure means "there is no number", as opposed to
|
||||
/// "the probe broke".
|
||||
///
|
||||
/// Both record an unavailable observation, but only the second is a fault
|
||||
/// worth surfacing as a broken probe.
|
||||
#[must_use]
|
||||
pub const fn is_expected_gap(self) -> bool {
|
||||
matches!(self, Self::NotAuthenticated)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user