Skip to content

codex: usage is never captured in a real run (tokens: null) because the Stop hook payload has no transcript_path — read_usage() returns before the codex-rollout parser runs #775

Description

@sirendhead
  • Observed: bmad-loop 0.11.1 (installed via uv tool; source clone identical on the cited files), Windows 11, psmux backend, Codex CLI 0.153.4, profile codex (usage_parser = "codex-rollout", hooks dialect codex-hooks-json, .codex/hooks.json relaying SessionStart + Stop to bmad_loop_hook.py). Run 20260909-144751-8e8b, review stage on [adapter.review] name = "codex", model gpt-6-astra, 38-minute session that completed normally via the Stop hook. Journal: {"kind": "session-end", "task_id": "TASK-2831-review-1", "status": "completed", "tokens": null, "tokens_weighted": null} — while the dev stage (claude) on the same run reported tokens: 19777795, tokens_weighted: 2460930. The task dir tasks/TASK-2831-review-1/ holds only heartbeat.json + prompt.txt (no messages.json).
  • The rollout files for that session exist and DO carry usage: ~/.codex/sessions/2026/09/09/rollout-2026-09-09T15-01-52-01a08530-0657-7ba1-91dd-8dd32958cdec.jsonl (39 token_count events) and rollout-2026-09-09T15-02-26-01a08530-8bb7-7910-afd1-5cab56cd8bef.jsonl (18; the session spawned a subagent) — payload.type == "token_count" with payload.info.total_token_usage.*, exactly what tally_codex_rollout reads.
  • Code path: src/bmad_loop/adapters/generic.py:1252-1254read_usage() does if not result.transcript_path: return None before tally_usage(self.profile.usage_parser, path) (:1262). src/bmad_loop/signals.py:113 fills transcript_path=data.get("transcript_path") from the hook payload. Codex's Stop hook payload carries no transcript_path (it is a Claude Code field), so for the codex profile the declared usage_parser is unreachable in a real run — every codex session logs tokens: null, and the [limits] token budget / cost ledgers see 0 for Codex.
  • Why bmad-loop probe-adapter codex --probe reports usage fine: probe mode discovers the rollout itself (probe.py ~line 790, glob ~/.codex/sessions/*/*/*/rollout-*.jsonlinfer_token_schema()), bypassing read_usage(); the same-day probe on this machine returned input_tokens: 2942, cache_read_tokens: 12928, output_tokens: 5 from rollout-2026-09-09T14-46-06-01a08521-99b8-72a0-b98b-156a48d74ca2.jsonl. The probe's captured Stop payload has no transcript_path key either — the JSON report keys are binary, binary_found, captured_events, cli, declared_events, dialect, help, hooks_registered, known_profile, mode, next_steps, schema_version, tokens, transcript, usage_parser, version, warnings, with transcript.location coming from the glob.
  • Proposed fix (either): (a) when the profile's usage parser is codex-rollout and the payload has no transcript_path, resolve the rollout from the Stop payload's session/thread id — rollout filenames end with the session UUID (rollout-<timestamp>-<session_id>.jsonl), so ~/.codex/sessions/**/rollout-*-<session_id>.jsonl is deterministic; sum usage across the subagent rollouts spawned by that session if they can be attributed; or (b) reuse the probe's discovery (newest rollout whose payload.cwd == the unit's cwd and whose first timestamp ≥ session start) as a fallback inside read_usage(), behind the profile (transcript_discovery = "codex-sessions-glob"). Add a zero-token test with a fixture rollout + a Stop payload lacking transcript_path that asserts usage is populated (ablation: remove the fallback → the test must fail).
  • Also worth a line in docs/adapter-authoring-guide.md: usage_parser on a profile is only reached when the CLI's Stop payload supplies transcript_path.

Source

found-in: bmad-loop 0.11.1 (uv tool) + source clone E:\vibe-coding\bmad-loop (main), Windows 11 · session: Claude Code 4d0f4496 (2026-09-09) · while: first color-dev-loop run with the review stage on Codex (customthing/ai-pod-auto PR #766) · evidence: journal session-end tokens null vs dev stage populated; rollout files with 39/18 token_count events; generic.py:1252-1254, signals.py:113, probe.py glob discovery

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions