feat: reasoning token split - OpenSpec design (4 changes)#21
Conversation
Split Output token tracking into OutputOther + OutputReasoning to support reasoning token reporting from Codex CLI. Designed as 4 independent changes with clear boundaries: 1. token-usage-output-split: Core TokenUsage struct refactor (provider.go only) 2. codex-reasoning-parsing: Parse reasoning_output_tokens from Codex JSONL 3. provider-output-rename: Mechanical Output→OutputOther in Claude/Kimi/Cursor 4. report-reasoning-column: Add Reasoning column to daily/session reports Changes 2-4 depend on Change 1 and can be implemented in parallel. Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
Pull request overview
Adds OpenSpec artifacts describing a 4-step design to split token usage output tracking into non-reasoning vs reasoning tokens, enabling Codex reasoning token reporting and exposing it in CLI tables.
Changes:
- Introduce OpenSpec project config and three new change specs (core
TokenUsagesplit, Codex reasoning parsing, provider mechanical rename, and report column updates). - Define required struct/method/schema updates for
TokenUsageoutput splitting (OutputOther,OutputReasoning,TotalOutput()). - Specify downstream updates for providers and CLI reporting to parse/aggregate/display reasoning tokens.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| openspec/config.yaml | Adds OpenSpec config scaffold for spec-driven artifacts. |
| openspec/changes/token-usage-output-split/.openspec.yaml | Metadata for Change 1 (core token usage output split). |
| openspec/changes/token-usage-output-split/design.md | Design rationale and trade-offs for splitting output tokens. |
| openspec/changes/token-usage-output-split/proposal.md | Proposal describing breaking schema change and new method. |
| openspec/changes/token-usage-output-split/specs/token-usage-output-split/spec.md | Formal requirements for OutputOther/OutputReasoning and totals. |
| openspec/changes/token-usage-output-split/tasks.md | Implementation checklist for provider/provider.go changes. |
| openspec/changes/codex-reasoning-parsing/.openspec.yaml | Metadata for Change 2 (Codex reasoning token parsing). |
| openspec/changes/codex-reasoning-parsing/design.md | Design for mapping reasoning_output_tokens into OutputReasoning. |
| openspec/changes/codex-reasoning-parsing/proposal.md | Proposal detailing Codex JSONL parsing updates and test adjustments. |
| openspec/changes/codex-reasoning-parsing/specs/codex-reasoning-parsing/spec.md | Requirements for parsing reasoning tokens and backward compatibility. |
| openspec/changes/codex-reasoning-parsing/tasks.md | Implementation checklist for Codex parser + tests. |
| openspec/changes/provider-output-rename/.openspec.yaml | Metadata for Change 3 (mechanical provider rename). |
| openspec/changes/provider-output-rename/design.md | Design for mechanical Output→OutputOther updates in providers/tests. |
| openspec/changes/provider-output-rename/proposal.md | Proposal describing provider/test renames to restore compilation. |
| openspec/changes/provider-output-rename/specs/provider-output-rename/spec.md | Requirements ensuring providers/tests use OutputOther and keep OutputReasoning at 0. |
| openspec/changes/provider-output-rename/tasks.md | Implementation checklist for Claude/Kimi/Cursor rename work. |
| openspec/changes/report-reasoning-column/.openspec.yaml | Metadata for Change 4 (reporting column + aggregation). |
| openspec/changes/report-reasoning-column/design.md | Design for adding a “Reasoning” column and aggregating it. |
| openspec/changes/report-reasoning-column/proposal.md | Proposal for report output changes and test updates. |
| openspec/changes/report-reasoning-column/specs/report-reasoning-column/spec.md | Requirements for “Reasoning” column placement and aggregation rules. |
| openspec/changes/report-reasoning-column/tasks.md | Implementation checklist for daily/session table output and tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| **Non-Goals:** | ||
| - Updating any provider parsers to populate `OutputReasoning` (handled by Change 3) | ||
| - Updating any CLI report code to display reasoning columns (handled by Change 4) | ||
| - Renaming `Output` references in provider parsers (handled by Change 2) |
There was a problem hiding this comment.
The Non-Goals bullets reference the wrong follow-up change numbers. Per the PR plan, populating OutputReasoning is handled by codex-reasoning-parsing (Change 2) and the mechanical Output→OutputOther renames are provider-output-rename (Change 3). Updating these references will avoid confusion when implementing the series.
| #### Scenario: Daily share table with reasoning tokens | ||
|
|
||
| - **WHEN** `printTopGroupShare()` renders the share table | ||
| - **THEN** the header row SHALL contain columns in this order: Rank, CLI/Model, Share, Sessions, Total, Input, Output, Reasoning, Cache Read, Cache Create |
There was a problem hiding this comment.
The spec hard-codes the daily share table's second header as CLI/Model, but the current implementation uses a dynamic group title (CLI vs Model) via groupColumnTitle(groupBy) in cmd/daily.go. To keep the spec implementable and unambiguous, consider wording this as “Rank, , …” (or explicitly “CLI or Model depending on groupBy”) rather than requiring the literal CLI/Model header.
| - **THEN** the header row SHALL contain columns in this order: Rank, CLI/Model, Share, Sessions, Total, Input, Output, Reasoning, Cache Read, Cache Create | |
| - **THEN** the header row SHALL contain columns in this order: Rank, <group column> (CLI or Model), Share, Sessions, Total, Input, Output, Reasoning, Cache Read, Cache Create |
|
|
||
| The `TokenUsage` struct (after Change 1) has `OutputOther` and `OutputReasoning` fields replacing the single `Output` field, plus a `TotalOutput()` method that sums both. The report layer in `cmd/daily.go` and `cmd/session.go` currently references `.Output` and does not display reasoning tokens. This change updates the report formatting to surface the new field. | ||
|
|
||
| The `dayTotal` and `groupTotal` structs embed `provider.TokenUsage`, so they automatically gain the new fields once Change 1 is applied. The only manual work is in `mergeTokenUsage()` (which explicitly copies individual fields) and the `fmt.Fprintf` formatting calls. |
There was a problem hiding this comment.
dayTotal and groupTotal in cmd/daily.go do not embed provider.TokenUsage (they have a named TokenUsage provider.TokenUsage field). This still means they pick up new TokenUsage fields after Change 1, but the wording “embed” is technically inaccurate and may mislead the implementation details.
| The `dayTotal` and `groupTotal` structs embed `provider.TokenUsage`, so they automatically gain the new fields once Change 1 is applied. The only manual work is in `mergeTokenUsage()` (which explicitly copies individual fields) and the `fmt.Fprintf` formatting calls. | |
| The `dayTotal` and `groupTotal` structs have a `TokenUsage provider.TokenUsage` field, so they automatically gain the new fields once Change 1 is applied. The only manual work is in `mergeTokenUsage()` (which explicitly copies individual fields) and the `fmt.Fprintf` formatting calls. |
- Fix wrong Change number references in token-usage-output-split Non-Goals - Use dynamic <group column> instead of hardcoded CLI/Model in spec scenario - Fix "embed" → "named field" wording for dayTotal/groupTotal structs Co-Authored-By: Claude Opus 4.6 <[email protected]>
Summary
OutputOther+OutputReasoningreasoning_output_tokensin session JSONL)Changes
1.
token-usage-output-split(core, blocks 2/3/4)TokenUsage.Output→OutputOther, addOutputReasoningfieldTotalOutput()method, updateTotal()provider/provider.goonly2.
codex-reasoning-parsing(parallel with 3/4)reasoning_output_tokensfrom Codex JSONLtoken_counteventsprovider/codex/only3.
provider-output-rename(parallel with 2/4)Output→OutputOtherin Claude/Kimi/Cursor providersprovider/{claude,kimi,cursor}/only4.
report-reasoning-column(parallel with 2/3)mergeTokenUsage()aggregationcmd/daily.go,cmd/session.goonlyTest plan
/opsx:applyafter spec approvalmake testafter implementation🤖 Generated with Claude Code