"""Codex companion analysis and manuscript sections. Loaded by build.py.
Every measured quantity comes from the same five public Codex sequences;
retention-loss projections are explicit counterfactuals, not extra observations.
"""
from fractions import Fraction
IDS=['agnt-codex','codex-r2','openclaw-codex-r2','omp-codex','hermes-codex']
LABELS={'agnt-codex':'AGNT','codex-r2':'Codex CLI','openclaw-codex-r2':'OpenClaw','omp-codex':'OMP','hermes-codex':'Hermes'}
COLORS={'agnt-codex':COL['AGNT'],'codex-r2':COL['Codex'],'openclaw-codex-r2':COL['OpenClaw'],'omp-codex':COL['OMP'],'hermes-codex':COL['Hermes']}
XC1=I('agnt-codex',1);XM=mx;THRESHOLD=272000
XG={i:Fraction(I(i,5)-I(i,1),4) for i in IDS}
XO={i:max(Fraction(0),XG[i]-XM) for i in IDS}
# Hermes -1 tk/request and alternating rounding are treated as tokenizer-scale
# variation for the common-content projection. Actual counters are not changed.
def xsession(n,policy='retained',overhead=0):
    g=Fraction(XM)+Fraction(overhead);U=R=0;uc=rc=0.0;longs=0;request_rows=[]
    for t in range(1,n+1):
        context=Fraction(XC1)+(t-1)*g
        read=Fraction(0) if t==1 or policy=='uncached' or (policy=='break-loss' and (t-1)%4==0) else context-g
        fresh=context-read;scale=2 if context>THRESHOLD else 1
        U+=fresh;R+=read;uc+=float(fresh)*10*scale/1e6;rc+=float(read)*scale/1e6;longs+=scale==2
        request_rows.append({'turn':t,'input':float(context),'uncached':float(fresh),'reads':float(read),'priceScale':scale,'usd':float(fresh)*10*scale/1e6+float(read)*scale/1e6})
    return {'uncached':float(U),'reads':float(R),'uncachedUSD':uc,'readUSD':rc,'usd':uc+rc,'longContextRequests':longs,'requests':request_rows}
X20={i:xsession(20,'retained',XO[i]) for i in IDS}
POLICIES=['retained','break-loss','uncached']
XPOL={p:[xsession(n,p) for _,n,_ in LENGTHS] for p in POLICIES}
XMULT={i:[xsession(n,'retained',XO[i])['usd']/xsession(n,'uncached')['usd'] for _,n,_ in LENGTHS] for i in IDS}
# Exact session composition, not scaling a one-hour burn-rate approximation.
XMONTH={p:[44*XPOL[p][li]['usd'] for li in [2,3,4]] for p in POLICIES}
XNATIVE_MONTH={i:[44*xsession(n,'retained',XO[i])['usd'] for n in [20,40,80]] for i in IDS}
XMEASURED=[]
for i in IDS:
    ts=S[i]['turns'];total=sum(t['input'] for t in ts);reads=sum(t['cacheRead'] for t in ts)
    XMEASURED.append({'id':i,'name':LABELS[i],'input':total,'reads':reads,'uncached':total-reads,'writeTokens':sum(t['cacheWrite'] for t in ts),'output':sum(t['output'] for t in ts),'weightedReadShare':reads/total,'postPauseShare':h5(i),'postPauseInput':I(i,5),'postPauseUncached':Pm(i,5),'postPauseCost':ts[4]['apiEquivalentUSD'],'fiveRequestUSD':sum(t['apiEquivalentUSD'] for t in ts),'meanInputGrowth':float(XG[i]),'excessGrowthOverReference':float(XG[i]-XM)})
XDATA={'prices':{'short':{'input':10,'read':1,'write':12.5,'output':50},'long':{'input':20,'read':2,'write':25,'output':75},'longThresholdExclusive':THRESHOLD},'commonContent':{'c1':XC1,'m':XM},'measured':XMEASURED,'oneHourRetained':X20,'policyByLength':XPOL,'multipliers':XMULT,'monthlyPolicies':XMONTH,'monthlyHarnessRetained':XNATIVE_MONTH,'profiles':{'workdays':22,'sessionsPerDay':2,'sessionRequests':[20,40,80],'hoursPerDay':[2,4,8]},'annualPolicyHeavy':{p:12*XMONTH[p][2] for p in POLICIES},'teamFiveAnnualHeavy':{p:5*12*XMONTH[p][2] for p in POLICIES},'amplifier':{'context':100000,'missInputUSD':1.0,'hitInputUSD':.1,'incrementalLossUSD':.9,'eventsPerDay':30,'daysPerMonth':22,'incrementalMonthUSD':594},'scope':'All cost projections are conditional API-list equivalents with zero separately reported write tokens, not observed subscription invoices or estimates of a miss probability.'}
(FIG/'codex-scenario.json').write_text(json.dumps(XDATA,indent=2))
with (FIG/'codex-figure-data.csv').open('w',newline='',encoding='utf8') as f:
    w=csv.writer(f);w.writerow(['id','turn','input','cached','uncached','cache_writes','output','read_percent','api_equivalent_usd'])
    for i in IDS:
        for t in S[i]['turns']:w.writerow([i,t['turn'],t['input'],t['cacheRead'],t['uncached'],t['cacheWrite'],t['output'],100*t['readShare'],t['apiEquivalentUSD']])
# The original seven figure types and palette remain; C1-C6 are their Codex counterparts.
xfigures=[]
def xsave(n,fig,caption):
    fig.savefig(FIG/f'figure-c{n}.svg',bbox_inches='tight',metadata={'Date':'2026-09-08'})
    fig.savefig(FIG/f'figure-c{n}.pdf',bbox_inches='tight',metadata={'Title':f'The Cache Wars 2, Figure C{n}','Author':'AGNT Labs','CreationDate':None,'ModDate':None})
    plt.close(fig);xfigures.append((n,caption))
fig,ax=plt.subplots(figsize=(6.4,3.1))
for i in IDS:ax.plot(range(1,6),[Pm(i,t) for t in range(1,6)],marker='o',markersize=3.5,color=COLORS[i],linewidth=2,label=LABELS[i])
ax.axvline(4.5,color='#777',linestyle=':',linewidth=.8);ax.set_xticks(range(1,6),['T1','T2','T3','T4','T5 (post-pause)']);ax.set_ylabel('Uncached input tokens (lower is better)');ax.yaxis.set_major_formatter(plt.FuncFormatter(kfmt));style(ax);ax.legend(frameon=False,ncol=3,loc='upper center',bbox_to_anchor=(.5,1.2));fig.tight_layout()
xsave(1,fig,'Codex per-request uncached input across all five harnesses, in GPT-6 Astra token units. All paths retain reads on request 5. OMP’s request-4 miss is shown, not replaced by a favorable run. All reported cache-write counters are zero.')
fig,ax=plt.subplots(figsize=(6.4,2.8));ys=range(5)
ax.barh(ys,[100*h5(i) for i in IDS],color=[COLORS[i] for i in IDS],height=.6)
for y,i in enumerate(IDS):ax.text(100*h5(i)+.8,y,pct2(h5(i)),va='center',fontsize=9)
ax.set_yticks(ys,[LABELS[i] for i in IDS]);ax.invert_yaxis();ax.set_xlim(0,104);ax.set_xlabel('Post-pause cached input (%) — higher is better');ax.grid(axis='x',color='#ddd',linewidth=.6);fig.tight_layout()
xsave(2,fig,'Codex request-5 cached-input share, with all five native paths. AGNT leads at 88.42%, followed by Codex CLI 85.43%, OpenClaw 83.19%, OMP 81.04%, and Hermes 79.81%. Total context is provided in Table C3: a larger reusable prefix can raise this ratio without minimizing total cost.')
fig,ax=plt.subplots(figsize=(6.4,2.8));ys=range(5)
ax.barh(ys,[XM]*5,color=COL['payload'],height=.6,label='Reference increment: 4,455 tk')
ax.barh(ys,[float(max(0,XG[i]-XM)) for i in IDS],left=[XM]*5,color=COL['overhead'],height=.6,label='Mean excess context growth')
for y,i in enumerate(IDS):ax.text(max(XM,float(XG[i]))+65,y,f'{float(XG[i]):,.2f}',fontsize=8,va='center')
ax.set_yticks(ys,[LABELS[i] for i in IDS]);ax.invert_yaxis();ax.set_xlim(0,7100);ax.set_xlabel('Mean input growth per request (tokens)');ax.grid(axis='x',color='#ddd',linewidth=.6);ax.legend(frameon=False,loc='upper center',bbox_to_anchor=(.5,1.18),ncol=2,fontsize=7.5);fig.tight_layout()
xsave(3,fig,f'Codex context growth across the four request-to-request transitions. AGNT is {float(XG["agnt-codex"]):,.0f} tokens per transition; Codex CLI averages {float(XG["codex-r2"]):,.2f}. Growth is an observed difference, not proof of a particular internal injection mechanism. Small negative residuals for OMP/Hermes are not turned into negative overhead bars.')
fig,ax=plt.subplots(figsize=(6.4,3.1));labs=[LABELS[i] for i in IDS]+['Common prefix: break loss','Common prefix: no cache'];vals=[X20[i] for i in IDS]+[XPOL['break-loss'][2],XPOL['uncached'][2]];ys=range(7)
ax.barh(ys,[v['uncachedUSD'] for v in vals],color=COL['writes'],height=.6,label='Uncached input');ax.barh(ys,[v['readUSD'] for v in vals],left=[v['uncachedUSD'] for v in vals],color=COL['reads'],height=.6,label='Cached reads')
for y,v in enumerate(vals):ax.text(v['usd']+.06,y,usd2(v['usd']),va='center',fontsize=8)
ax.set_yticks(ys,labs);ax.invert_yaxis();ax.set_xlabel('One-hour input API equivalent (USD)');ax.set_xlim(0,max(v['usd'] for v in vals)*1.15);ax.grid(axis='x',color='#ddd',linewidth=.6);ax.legend(frameon=False,ncol=2,loc='upper center',bbox_to_anchor=(.5,1.18));fig.tight_layout()
xsave(4,fig,'Codex one-hour projection at GPT-6 Astra prices: common initial context, common payload, mean observed excess growth, and retained-prefix assumption for every harness. The final two bars are a common-content miss-after-each-break scenario and an uncached control; they are not attributed to any harness. Lower dollar values are better for this analytical workload.')
fig,ax=plt.subplots(figsize=(6.4,3.1))
for i in IDS:ax.plot(range(5),XMULT[i],marker='o',markersize=3,color=COLORS[i],label=LABELS[i],linewidth=1.8)
ax.plot(range(5),[v['usd']/XPOL['uncached'][j]['usd'] for j,v in enumerate(XPOL['break-loss'])],color='#333',linestyle='--',label='Common-content break loss')
ax.set_xticks(range(5),[l for l,_,_ in LENGTHS]);ax.set_ylabel('Cost / common uncached baseline');style(ax);ax.legend(frameon=False,ncol=3,loc='upper center',bbox_to_anchor=(.5,1.2),fontsize=8);fig.tight_layout()
xsave(5,fig,'Codex cost multipliers from 15-minute to four-hour scenarios. All harness lines assume the prior prefix remains available. The common-content break-loss line is a sensitivity test. Requests above 272,000 input tokens use long-context rates for their full input; this price threshold is included rather than extrapolating short-context prices indefinitely.')
fig,ax=plt.subplots(figsize=(6.4,3.2));w=.23
for j,p in enumerate(POLICIES):
    xs=[g+(j-1)*w for g in range(3)];vals=XMONTH[p];ax.bar(xs,vals,w,label={'retained':'Retained prefix','break-loss':'Loss after each break','uncached':'No cache'}[p],color={'retained':COL['AGNT'],'break-loss':COL['OMP'],'uncached':COL['none']}[p])
    for x,v in zip(xs,vals):ax.text(x,v+20,f'${v:,.0f}',ha='center',va='bottom',fontsize=8,rotation=90)
ax.set_xticks(range(3),['Light (2 h/day)','Moderate (4 h/day)','Heavy (8 h/day)']);ax.set_ylabel('Monthly input API equivalent (USD)');ax.set_ylim(0,max(XMONTH['uncached'])*1.23);style(ax);ax.legend(frameon=False,ncol=3,loc='upper center',bbox_to_anchor=(.5,1.14),fontsize=8);fig.tight_layout()
xsave(6,fig,'Codex monthly per-seat scenarios for exactly 44 independent sessions of 20, 40, or 80 requests. Retained, break-loss and uncached common-content costs use the full length of each session and the long-context threshold. These are API-equivalent workload projections, not subscription prices or measured quota deductions.')

def xb(kind,value):return(kind,value)
def xp(t):return xb('p',t)
def xs(t):return xb('subsection',t)
def xt(n,heads,rows,caption,left=(0,),widths=None):return xb('xtable',(n,heads,rows,caption,tuple(left),widths))
def xf(n):return xb('xfigure',n)
BACKGROUND=[xs('Codex caching semantics and explicit price list'),xp('Codex caching is automatic prefix reuse, not Anthropic cache_control markers. OpenAI’s Platform documentation for GPT-5.6 and later specifies prompt_cache_options.ttl = 30m as the default and only supported minimum lifetime; older model families expose different retention controls [12]. This is a Platform API contract, not proof that the subscription backend exposes every control. In these subscription runs, five paths retained cache across the measured pause; the OMP burst miss demonstrates that eligible retention and an actual hit are not the same thing.'),xt(1,['Model / context','Input','Cached','Write','Output'],[['Sonnet 5','$2.00','$0.20','$2.50 / $4.00','$10.00'],['GPT-6 Astra ≤272k','$10.00','$1.00','$12.50','$50.00'],['GPT-6 Astra >272k','$20.00','$2.00','$25.00','$75.00']],'Complete standard list prices in USD per million tokens [2,3,13]. Sonnet writes are 5-minute / one-hour; Astra’s write price is a separate accounting category, not an Anthropic TTL. The Astra threshold applies to the full request. The 65 measured requests are below it; the longer analytical sessions may cross it.'),xp('For Codex, total input I already contains cached input C; premium or non-read input P = I − C. The raw AGNT provider event uses input_tokens_details.cached_tokens, while Codex CLI reports cached_input_tokens. OMP, OpenClaw and Hermes expose normalized exclusive-input shapes that must be combined with their cache reads once, not twice. No accepted Codex request reports separately billed cache-write tokens. API-equivalent comparisons use $10/M uncached and $1/M cached input for these short-context measured requests. Subscription fees and quota meters are separate (§8).')]
DESIGN=[xs('Codex-specific controls'),xp('The Codex track compares AGNT, Codex CLI, OMP, OpenClaw and Hermes on gpt-6-astra using one shared five-prompt file. Claude Code has no Codex arm in this study. The same 390-second minimum idle is retained for comparability, but it does not distinguish Codex TTL categories. The response is OKt (six billed output tokens); no tool execution is performed. The four measured growth increments, cache counters, prompt hashes and gaps are retained for every path. The native session footprints are reported separately from common-content projections, and a burst miss is never erased or converted into an estimated miss frequency.')]
RESULTS=[xs('Codex — per-turn telemetry and complete session accounting'),xp('Table 3 contains the full cached/non-read pairs for all five Codex paths; Figure C1 gives the corresponding five-line trajectory. Tables C2 and C3 separately identify whole-sequence reuse, final-request reuse and actual context volume. Higher cache share is better for reuse; lower uncached tokens and API-equivalent dollars are better at the stated footprint. These are different rankings.'),xf(1),xt(2,['Harness','Input (5 turns)','Cached','Weighted h','5-turn USD'],[[r['name'],num(r['input']),num(r['reads']),pct2(r['weightedReadShare']),f"${r['fiveRequestUSD']:.6f}"] for r in XMEASURED],'Codex whole-sequence native-footprint accounting, including the cold/warm first requests and OMP’s request-4 miss. USD is an API-rate equivalent including the observed 30 output tokens per sequence, not a subscription bill.'),xt(3,['Harness','Pause s','Input₅','Cached₅','P₅','h₅','USD₅'],[[LABELS[i],f'{gap(i):.3f}',num(I(i,5)),num(C(i,5)),num(Pm(i,5)),pct2(h5(i)),f"${S[i]['turns'][4]['apiEquivalentUSD']:.6f}"] for i in IDS],'Codex post-pause request: all five harnesses, complete denominator and costs. AGNT leads h₅. The native-context USD column is not an equal-content harness ranking.'),xf(2),xp(f"AGNT ranks first on the Codex pause metric at {pct2(h5('agnt-codex'))}, {100*(h5('agnt-codex')-h5('codex-r2')):.2f} percentage points ahead of Codex CLI. It uses {num(Pm('agnt-codex',5))} uncached tokens versus {num(Pm('codex-r2',5))} for the CLI on that request. Across the full sequence, however, Codex CLI has the higher weighted cache share ({pct2(S['codex-r2']['summary']['weightedReadShare'])} versus {pct2(S['agnt-codex']['summary']['weightedReadShare'])}), in part because its first request reads 12,288 shared-prefix tokens while AGNT’s reads zero. Hermes’ smaller native context has the lowest full-sequence API equivalent. None of these results is hidden by the primary endpoint."),xs('Codex — context growth and burst misses'),xf(3),xt(4,['Harness','ΔI₂','ΔI₃','ΔI₄','ΔI₅','Mean'],[[LABELS[i]]+[num(g) for g in growth(i)]+[f'{float(XG[i]):,.2f}'] for i in IDS],'Codex input increments. The reference 4,455-token increment is measured in AGNT; differences can include role/message framing and client context. They do not establish a specific source-code cause.'),xp(f"OMP’s fourth request contains {num(I('omp-codex',4))} total input tokens and zero reads; its fifth reads {num(C('omp-codex',5))} tokens. The missed read increases the observed sequence cost. One miss in one sequence cannot estimate a reliable miss probability. We therefore use the same retained-prefix assumption for all harnesses in the growth-based projection and show loss after each break only as a separate sensitivity scenario (§6.2).")]
COST=[xs('Codex cost model and one-hour projection'),xp(f"The Codex model uses common initial context c₁ = {num(XC1)} and increment m = {num(XM)}, both from the measured AGNT sequence, to avoid comparing unequal native system prompts. Let g = m + o; o is the nonnegative mean excess input growth measured for a harness. Tiny negative residuals for OMP/Hermes are set to zero only in the model, never in the receipts. Every projected session starts cold. Let qₜ = 1 when the whole previous prefix is retained and qₜ = 0 when it is unavailable. Input Jₜ = c₁ + (t−1)g; reads Cₜ = qₜ[Jₜ−g] for t > 1, C₁ = 0; uncached input Uₜ = Jₜ − Cₜ."),xb('equation',(r'\begin{aligned}K_X(n)&=10^{-6}\sum_{t=1}^{n}\left[u(J_t)U_t+r(J_t)C_t+v(J_t)W_t+p(J_t)O_t\right],\\ (u,r,v,p)&=(10,1,12.5,50)\quad(J_t\le272000),\\ (u,r,v,p)&=(20,2,25,75)\quad(J_t>272000).\end{aligned}','K_X(n) = Σₜ [u(Jₜ)Uₜ + r(Jₜ)Cₜ + v(Jₜ)Wₜ + p(Jₜ)Oₜ] / 1,000,000. Rates: (10,1,12.5,50) through 272,000 input tokens; (20,2,25,75) above.')),xp('The input-only projection sets W = O = 0, matching zero separately reported writes and excluding identical fixed-output replies. The measured-dollar table includes output. The retained case sets q = 1 after the first request. The break-loss case sets q = 0 on requests 5,9,13,17 and so on, with retained prefixes between breaks. That is an assumed miss schedule, not a measured Codex expiry interval. No-cache sets q = 0 throughout. All five harnesses receive the retained assumption in the primary growth comparison.'),xt(5,['Configuration','U tokens','C tokens','Input USD','Read USD','Total'],[[LABELS[i]+' / retained',num(X20[i]['uncached']),num(X20[i]['reads']),usd3(X20[i]['uncachedUSD']),usd3(X20[i]['readUSD']),usd3(X20[i]['usd'])] for i in IDS]+[[label,num(XPOL[p][2]['uncached']),num(XPOL[p][2]['reads']),usd3(XPOL[p][2]['uncachedUSD']),usd3(XPOL[p][2]['readUSD']),usd3(XPOL[p][2]['usd'])] for p,label in [('break-loss','Common / break loss'),('uncached','Common / no cache')]],'Codex one-hour, 20-request input-only API-equivalent projection. All initial prefixes and user payloads are equal; only mean observed excess growth differs in the first five rows. Identical prefix availability and zero excess growth yield identical costs.'),xf(4),xt(6,['Harness / retained']+[l for l,_,_ in LENGTHS],[[LABELS[i]]+[f'{x:.3f}×' for x in XMULT[i]] for i in IDS]+[['Common / break loss']+[f"{v['usd']/XPOL['uncached'][j]['usd']:.3f}×" for j,v in enumerate(XPOL['break-loss'])]],'Codex cost multipliers relative to the zero-extra-growth uncached common workload of the same length. Long-context prices apply above 272,000 input tokens. The model does not guarantee provider retention over a multi-hour session.'),xf(5),xp(f"At one hour the retained zero-extra-growth model costs {usd3(XPOL['retained'][2]['usd'])}; assumed loss after each fourth request costs {usd3(XPOL['break-loss'][2]['usd'])}; uncached costs {usd3(XPOL['uncached'][2]['usd'])}. AGNT, OMP and Hermes tie in the common-content retained model after tokenizer-scale residuals are rounded to zero. Codex CLI’s higher measured mean growth raises its projection to {usd3(X20['codex-r2']['usd'])}. AGNT’s measured pause-ratio win must not be recast as an exclusive price advantage when content and cache availability are identical.")]
MONTH=[xs('Codex monthly and annual extrapolation'),xp('For Codex the same light, moderate and heavy profiles are evaluated as 44 independent sessions per month with 20, 40 or 80 requests per session. Monthly cost is the exact per-session sum multiplied by 22 days × 2 sessions/day; annual cost is 12 times monthly. This includes the full-request long-context threshold. It is not a fixed one-hour burn rate applied after the context has grown.'),xb('equation',(r'K_X^{\mathrm{month}}(n)=44K_X(n),\quad K_X^{\mathrm{year}}(n)=12\cdot44K_X(n),\quad K_{5\mathrm{seat}}^{\mathrm{year}}(n)=5\cdot12\cdot44K_X(n).','Codex: monthly K = 44 × session K(n); annual K = 12 × monthly K; five-seat annual K = 5 × annual K.')),xt(7,['Configuration','Light / mo','Moderate / mo','Heavy / mo','Heavy / yr'],[[LABELS[i]+' / retained']+[usd2(x) for x in XNATIVE_MONTH[i]]+[usd2(12*XNATIVE_MONTH[i][2])] for i in IDS]+[[label]+[usd2(x) for x in XMONTH[p]]+[usd2(12*XMONTH[p][2])] for p,label in [('break-loss','Common / break loss'),('uncached','Common / no cache')]],'Codex monthly and annual input API equivalents at current standard GPT-6 Astra prices. These are analytical workloads, not subscription prices. Retention-loss rows are scenarios rather than measured behavior attributed to a harness.'),xf(6),xs('Codex cache-loss tax and team scale'),xt(8,['Scenario vs retained','Heavy Δ / mo','Heavy Δ / yr','5-seat Δ / yr'],[[label,usd2(XMONTH[p][2]-XMONTH['retained'][2]),usd2(12*(XMONTH[p][2]-XMONTH['retained'][2])),usd2(60*(XMONTH[p][2]-XMONTH['retained'][2]))] for p,label in [('break-loss','Loss after each break'),('uncached','No reuse throughout')]],'Codex cache-loss tax on strictly identical content. No miss frequency has been inferred from the five-request measurement; the loss schedule is specified in §6.2.'),xp(f"For the heavy common-content profile, retained-prefix input costs {usd2(XMONTH['retained'][2])}/seat/month; assumed break loss costs {usd2(XMONTH['break-loss'][2])}; no reuse costs {usd2(XMONTH['uncached'][2])}. The break-loss difference is {usd2(XMONTH['break-loss'][2]-XMONTH['retained'][2])} per seat per month. A five-seat team scales the annual difference by 60, as shown above. This quantifies exposure to misses, not a claim that any particular Codex client experiences that pattern."),xs('Codex agentic amplifier'),xp('At 100,000 input tokens, a completely uncached GPT-6 Astra request has a $1.00 input API equivalent, versus $0.10 if that entire existing prefix is read from cache. The incremental loss is $0.90 per event; 30 such events per day over 22 days would be $594 per month. No extra $12.50/M cache-write charge is added because the observed subscription counters report none. Unlike the Claude five-minute setting, a five-minute tool wait is not itself evidence of Codex expiration. This scenario applies only when reuse is actually lost; it is not a measured tool-loop bill or a separate additive charge if those same misses are already counted in the session model.')]
SUBSCRIPTION=[xs('Codex subscription versus metered OpenAI access'),xp('The Codex sequences authenticated through the connected ChatGPT subscription on all five paths. This establishes technical access during these trials; it does not identify the account allowance from which usage was deducted, certify third-party product approval, or convert cached tokens to plan credits. The measured input/read/output counters and the published Astra price list support the API-equivalent tables. No OpenAI API top-up was required for these accepted Codex runs, and no separate API bill was measured.'),xs('Separate accounting for both provider families'),xp('The same distinction applies to Claude: subscription-authenticated successful usage is not proof that a locally estimated dollar amount was charged or saved as cash. A plan comparison must include actual subscription fees, included allowance, overage and workload quality. The paper’s Claude and Codex dollar models are in separate price units. A low-cost row on one provider must not be compared to another model as if model capability and account terms were held constant.')]
# Preserve existing main sections and the original 12 table / 7 figure numbers.
def insert_before_section(name,addition):
 idx=next(k for k,b in enumerate(blocks) if b==('section',name));blocks[idx:idx]=addition
insert_before_section('Systems Under Test',BACKGROUND)
insert_before_section('Results',DESIGN)
insert_before_section('Cost Model and One-Hour Extrapolation',RESULTS)
idx=blocks.index(('section','Cost Model and One-Hour Extrapolation'));blocks.insert(idx+1,xs('Claude cost model and one-hour projection'))
insert_before_section('Monthly and Annual Extrapolation',COST)
insert_before_section('Ancillary Finding: Subscription Access',MONTH)
insert_before_section('Threats to Validity',SUBSCRIPTION)
insert_before_section('Reproducibility',[xp('Codex projections use the current Platform price table with the 272,000-input-token threshold, but the subscription backend is a distinct access surface. The retained-prefix and break-loss schedules are assumptions, not forecast probabilities. Source fingerprints and recorded counter shapes identify what was observed; they do not establish a long-run error rate or universal cross-harness cost ordering.')])
idx=blocks.index(('section','Reproducibility'));blocks.insert(idx+1,xp('Codex replication assets have the same status as Claude assets: all five Codex paths, all 25 measured request receipts, original counter objects, prompt digests, and per-request price reconciliation are included. codex-scenario.json contains every request in each projection, including the short/long price multiplier; codex-figure-data.csv contains the measured chart rows. Figures C1–C6 and Tables C1–C8 are rebuilt from those files and verified independently.'))
refs.extend([('OpenAI. Prompt caching.','Platform model-dependent retention settings and automatic prefix reuse (accessed September 8, 2026).','https://developers.openai.com/api/docs/guides/prompt-caching'),('OpenAI. GPT-6 Astra.','Standard pricing and full-request long-context threshold above 272K input tokens (accessed September 8, 2026).','https://developers.openai.com/api/docs/models/gpt-6-astra')])
# The references block stores the same list object; added entries become [12,13].
abstract += f" The Codex path is analyzed separately through measured whole-session costs, all five pause results, per-turn growth, and its own one-hour, multi-hour, monthly and annual model. At GPT-6 Astra rates the common-content one-hour input scenarios are {usd2(XPOL['retained'][2]['usd'])} with retained reuse, {usd2(XPOL['break-loss'][2]['usd'])} with assumed loss after each break, and {usd2(XPOL['uncached'][2]['usd'])} without reuse; these are conditional API equivalents, not subscription charges."
# Emit a scope map used as a mechanical acceptance gate.
coverage={'originalMainSections':[v for k,v in blocks if k=='section'],'originalFigures':list(range(1,8)),'codexFigures':[f'C{i}' for i in range(1,7)],'originalTables':list(range(1,13)),'codexTables':[f'C{i}' for i in range(1,9)],'codexCoverage':['Background / retention and prices','Experimental controls','Results / five native paths','Results / growth and burst miss','One-hour common-content costs','Multi-hour multipliers / long-context tier','Monthly and annual by harness and scenario','Cache-loss tax and five-seat team','Tool-latency exposure','Subscription vs API billing','Reproducibility / counters and model'],'unchangedMeasuredRequests':65}
(OUT/'artifacts/data').mkdir(parents=True,exist_ok=True)
(OUT/'artifacts/data/section-coverage.json').write_text(json.dumps(coverage,indent=2))
