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
@@ -2,8 +2,13 @@
|
||||
|
||||
use std::fmt;
|
||||
|
||||
mod usage;
|
||||
mod workspace;
|
||||
|
||||
pub use usage::{
|
||||
AccountProfile, AccountProfileId, FooterUsage, UsageConfidence, UsageError, UsageLedger,
|
||||
UsageObservation, UsageProjection, UsageProvenance, UsageUnit, UsageWindow, format_duration_ms,
|
||||
};
|
||||
pub use workspace::{
|
||||
CommandOrigin, CommandRequestId, LayoutNode, PaneCloseDisposition, PaneDefinition, PaneId,
|
||||
PaneLaunchIntent, PanePlacement, PaneSurface, SplitAxis, SplitRatio, WorkspaceApplyOutcome,
|
||||
@@ -96,15 +101,6 @@ impl fmt::Display for Platform {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum UsageProvenance {
|
||||
ProviderReported,
|
||||
HarnessReported,
|
||||
LocallyMeasured,
|
||||
Estimated,
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ProductStatus {
|
||||
pub platform: Platform,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -464,6 +464,11 @@ impl WorkspaceState {
|
||||
|
||||
/// Applies one idempotent command after checking its required capability.
|
||||
///
|
||||
/// Authority is checked before the replay table is consulted. A caller that
|
||||
/// cannot perform a command also cannot learn whether its request ID was
|
||||
/// already applied, so the retry path is not an oracle over the workspace's
|
||||
/// history.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Rejects insufficient authority and invalid state transitions without
|
||||
@@ -473,6 +478,11 @@ impl WorkspaceState {
|
||||
request: WorkspaceRequest,
|
||||
granted: WorkspaceCapability,
|
||||
) -> Result<WorkspaceApplyOutcome, WorkspaceError> {
|
||||
request.origin.validate()?;
|
||||
let required = request.command.required_capability();
|
||||
if granted < required {
|
||||
return Err(WorkspaceError::CapabilityDenied { required, granted });
|
||||
}
|
||||
if let Some(applied) = self.applied_requests.get(&request.request_id) {
|
||||
if applied == &request {
|
||||
return Ok(WorkspaceApplyOutcome {
|
||||
@@ -483,11 +493,6 @@ impl WorkspaceState {
|
||||
}
|
||||
return Err(WorkspaceError::RequestConflict(request.request_id));
|
||||
}
|
||||
request.origin.validate()?;
|
||||
let required = request.command.required_capability();
|
||||
if granted < required {
|
||||
return Err(WorkspaceError::CapabilityDenied { required, granted });
|
||||
}
|
||||
|
||||
let event = self.apply_command(request.command.clone())?;
|
||||
self.applied_requests
|
||||
@@ -749,6 +754,33 @@ mod tests {
|
||||
assert_eq!(state.revision(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replaying_an_applied_request_still_requires_its_capability() {
|
||||
let mut state = workspace();
|
||||
let command = request(
|
||||
"launch-agent",
|
||||
WorkspaceCommand::SplitPane {
|
||||
target: id(PaneId::new, "root"),
|
||||
pane: pane("agent", true),
|
||||
axis: SplitAxis::Horizontal,
|
||||
ratio: SplitRatio::default(),
|
||||
placement: PanePlacement::After,
|
||||
},
|
||||
);
|
||||
state
|
||||
.apply(command.clone(), WorkspaceCapability::Execute)
|
||||
.expect("execute may launch");
|
||||
// The replay table must not become an oracle: a caller holding less
|
||||
// authority learns nothing about what has already been applied.
|
||||
assert!(matches!(
|
||||
state.apply(command, WorkspaceCapability::Observe),
|
||||
Err(WorkspaceError::CapabilityDenied {
|
||||
required: WorkspaceCapability::Execute,
|
||||
granted: WorkspaceCapability::Observe,
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reused_request_id_must_match_origin_and_command() {
|
||||
let mut state = workspace();
|
||||
|
||||
Reference in New Issue
Block a user