[FIX] fix: coerce None→0 in token telemetry serialization boundary#3994
Merged
Trecek merged 1 commit intoJun 11, 2026
Merged
Conversation
CanonicalTokenUsage.to_dict() now emits 0 for None-valued cache fields instead of None, enforcing the TokenUsageFileEntry TypedDict contract at the serialization boundary. Defensive None-safe accumulations added to load_from_log_dir, record(), flush_session_log, sessions.jsonl writer, and _load_sessions/_format_model_table to guard against already-corrupt or future None-valued fields in token_usage.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Trecek
added a commit
that referenced
this pull request
Jun 28, 2026
Produce merged machine-readable seed disposition table at .autoskillit/temp/issue_disposition_draft.md that consolidates WP1 (20 issues — Strategic & SessionLocator cluster) and WP2 (49 unique issues — String-to- Capability, Conformance, Builder-Divergence, Guard-Bypass, D9) with new WP3 classifications for six remaining issue clusters. The merged table uses a normalized five-column schema: issue_number | title | classification | absorbing_requirement_or_wp | close_action Header section documents snapshot date (2026-06-28), seed coverage (75 issues across 3 WPs), source file references, and the P2-A9 extension instruction. WP3 work: - Reclassified 7 of 8 D9 issues from INDEPENDENT to absorbed (#3297, #3638, #3877, #3676, #3836, #3781, #3756); #3876 stays independent (Codex sandbox constraint). - Added 8 new rows: #3335 (Codex Config Schema Drift Immunity), #3971 (fleet authority), #3993 (cache_write_tokens closed via PR #3994), #823/#2453/#2671/ #2479/#2481 (CanonicalTokenUsage / backend registry). - For overlap issues #3385 and #3699 (in both WP1 and WP2 main rows), used WP2's absorbed classification with WP1's R-A R-group reference. Verification: - 75 unique issue rows in main table (sorted ascending by issue_number). - All 13 AC-enumerated issues present. - All 8 D9 items reconciled to absorbing requirement or flagged independent. - #3993 has classification=closed, close_action=already-closed. - WP span verified: WP1 (#23), WP2 (#3103), WP3 (#3335) all present. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CanonicalTokenUsageusesint | Nonefor cache fields to preserve provider semantics (Codex has no cache-write concept). However, the persistence boundary inflush_session_log()and both readers (load_from_log_dir,_load_sessions) usedict.get("key", 0)which silently passesNonethrough when the key is present —dict.get()only returns the default for absent keys, not forNone-valued ones. This violates theTokenUsageFileEntryTypedDict contract (which declarescache_write_tokens: int, notint | None) and causesint += None→TypeErrorat reload.The architectural weakness is a missing coercion layer at the serialization boundary:
CanonicalTokenUsage.to_dict()emits domain-model values (None= "not applicable") directly into a dict that consumers treat as storage-model values (all ints). The fix is to enforce theTokenUsageFileEntrycontract structurally — make it impossible to write non-int values for int-typed fields.Requirements
patch_pr_token_summarycrashes withTypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'when aggregating telemetry from pipeline runs that include Codex-backend sessions. The crash is caused by adict.get()semantic trap:Nonevalues written totoken_usage.jsonby the Codex backend bypass the0fallback in.get("cache_write_tokens", 0)becausedict.get()only uses the default when the key is absent, not when it is explicitlyNone.Closes #3993
Implementation Plan
Plan file:
/home/talon/projects/autoskillit-runs/remediation-20260610-190114-286663/.autoskillit/temp/rectify/rectify_none_bypass_token_serialization_2026-06-10_192500.md🤖 Generated with Claude Code via AutoSkillit
Token Usage Summary
* Step used a non-Anthropic provider; caching behavior may differ.
Token Efficiency
Model Usage Breakdown