Files
lumbridge-code/crates/lumbridge-harness/tests/codex_ledger_path.rs
T
Metal AgentandClaude Opus 5 ef52aa7ce2 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>
2026-08-31 21:47:11 -07:00

176 lines
6.1 KiB
Rust

//! End-to-end: a Codex transcript through the real ledger and footer.
//!
//! The unit tests prove the adapter builds the right observations. These prove
//! the observations still obey every decision 0012 rule once they reach
//! `lumbridge-core`, which is the property that actually matters to a user
//! reading the footer.
//!
//! Every transcript below is hand-written from the documented wire shape in
//! openai/codex (Apache-2.0), commit
//! `17e8101699c5062117d0d37f504313e8af53b043`, `codex-rs/app-server/README.md`
//! section "7) Rate limits (`ChatGPT`)". No real account, transcript, or
//! credential is used, and all timestamps are small synthetic integers.
use lumbridge_core::{
AccountProfileId, FooterUsage, UsageConfidence, UsageLedger, UsageProvenance,
};
use lumbridge_harness::codex::replay_transcript;
const HOUR_MS: u64 = 3_600_000;
/// A synthetic reset five hours after the epoch, in seconds.
const RESETS_AT_SECONDS: u64 = 5 * 3_600;
const RESETS_AT_MS: u64 = 5 * HOUR_MS;
fn initialize_response() -> &'static str {
r#"{"jsonrpc":"2.0","id":1,"result":{"userAgent":"codex"}}"#
}
fn read_response(id: u64, percent: u64) -> String {
format!(
r#"{{"jsonrpc":"2.0","id":{id},"result":{{"rateLimits":{{"primary":{{"usedPercent":{percent},"windowDurationMins":300,"resetsAt":{RESETS_AT_SECONDS}}},"secondary":null,"rateLimitReachedType":null}}}}}}"#
)
}
fn primary() -> AccountProfileId {
AccountProfileId::new("codex-app-server-primary").expect("the probe's static ID is valid")
}
fn ledger_from(frames: &[(&str, u64)]) -> UsageLedger {
let mut ledger = UsageLedger::new();
for observation in replay_transcript(frames) {
ledger
.record(observation)
.expect("the adapter emits observations in time order");
}
ledger
}
#[test]
fn a_two_reading_transcript_produces_a_provider_reported_footer() {
let first = read_response(2, 20);
let second = read_response(3, 35);
let ledger = ledger_from(&[
(initialize_response(), 0),
(first.as_str(), HOUR_MS),
(second.as_str(), 2 * HOUR_MS),
]);
let projection = ledger.project(&primary(), 2 * HOUR_MS);
assert_eq!(projection.provenance(), UsageProvenance::ProviderReported);
assert_eq!(projection.confidence(), UsageConfidence::Exact);
assert_eq!(projection.consumed_permille(), Some(350));
assert_eq!(projection.resets_in_ms(), Some(3 * HOUR_MS));
// 200 permille burned over one hour, derived rather than reported.
assert_eq!(projection.burn_per_hour(), Some(150));
assert_eq!(projection.burn_provenance(), UsageProvenance::Estimated);
}
#[test]
fn a_single_reading_yields_a_percentage_but_no_rate() {
let only = read_response(2, 20);
let ledger = ledger_from(&[(initialize_response(), 0), (only.as_str(), HOUR_MS)]);
let projection = ledger.project(&primary(), HOUR_MS);
assert_eq!(projection.consumed_permille(), Some(200));
assert_eq!(
projection.burn_per_hour(),
None,
"one provider reading is not a rate"
);
assert_eq!(projection.exhaustion_in_ms(), None);
}
#[test]
fn an_api_key_account_renders_the_unavailable_path_rather_than_a_zero() {
// Codex answers -32600 with "chatgpt authentication required to read rate
// limits" for an account that has no subscription quota.
let denied = r#"{"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"chatgpt authentication required to read rate limits"}}"#;
let ledger = ledger_from(&[(initialize_response(), 0), (denied, HOUR_MS)]);
let projection = ledger.project(&primary(), HOUR_MS);
assert!(!projection.is_available());
assert_eq!(projection.consumed_permille(), None);
let profile = lumbridge_core::AccountProfile::new(
"codex-app-server-primary",
"Codex",
"ChatGPT",
"primary window",
"subscription",
)
.expect("a valid profile");
let footer = FooterUsage::new(&profile, &projection);
assert_eq!(footer.window(), "usage unavailable");
assert_eq!(footer.burn(), "burn rate unavailable");
assert_eq!(footer.trust(), "no usage source");
assert!(
!footer.to_string().contains('%'),
"an account with no quota must not render a percentage"
);
}
#[test]
fn a_reading_taken_after_the_reset_never_renders_a_frozen_percentage() {
let stale = read_response(2, 90);
let ledger = ledger_from(&[
(initialize_response(), 0),
// Observed one millisecond after the window it describes ended.
(stale.as_str(), RESETS_AT_MS + 1),
]);
let projection = ledger.project(&primary(), RESETS_AT_MS + 1);
assert!(!projection.is_available());
assert_eq!(
projection.consumed_permille(),
None,
"an ended window must not keep rendering its last percentage"
);
}
#[test]
fn a_credential_refresh_request_produces_no_observation_at_all() {
let refresh = r#"{"jsonrpc":"2.0","id":77,"method":"account/chatgptAuthTokens/refresh"}"#;
let reading = read_response(2, 20);
let observations = replay_transcript(&[
(initialize_response(), 0),
(refresh, 1_000),
(reading.as_str(), HOUR_MS),
]);
assert_eq!(
observations.len(),
1,
"the refused credential request must not enter the usage stream"
);
}
#[test]
fn a_repeated_identical_reading_does_not_flood_the_ledger() {
let a = read_response(2, 20);
let b = read_response(3, 20);
let c = read_response(4, 20);
let ledger = ledger_from(&[
(initialize_response(), 0),
(a.as_str(), HOUR_MS),
(b.as_str(), HOUR_MS + 60_000),
(c.as_str(), HOUR_MS + 120_000),
]);
assert_eq!(
ledger.observation_count(&primary()),
1,
"identical readings inside the re-emit floor must not evict history"
);
}
#[test]
fn a_malformed_transcript_records_nothing_rather_than_guessing() {
let observations = replay_transcript(&[
(initialize_response(), 0),
("}{ not json", 1_000),
(
r#"{"jsonrpc":"2.0","id":2,"result":{"rateLimits":"a string"}}"#,
2_000,
),
]);
assert!(observations.is_empty());
}