Skip to main content

cost-risk-toolkit

tests

A Python library and command line tool for defense cost estimating. Point it at your own production history, units and cost for each lot, and it fits a learning curve, tells you which lots it misses, forecasts the next buy with prediction intervals, and writes down every assumption it made along the way. It also carries a full synthetic CSDR/SRDR pipeline, parametric CERs, and correlated Monte Carlo risk analysis.

Want a window instead of a terminal? The desktop lot cost model lives in its own repository, lot-cost-model. It is a tkinter tool over the same three models: paste analogy lots and estimate lots from Excel, fit LC, Rate and LC+Rate, roll several WBS elements into one programme, and write the Excel workbook. Since its 3.0.0 it is a front end onto this library rather than a second copy of it: the fit, the roll-up, the risk and the workbooks all come from cost_core, and the window refuses to start without it.

Fit a curve to your own lot data in one command:

ce-core fit-lots --csv mylots.csv --dollar-year 2026 --out results/
lot,units,cost
LRIP 1,22,96800000
LRIP 2,18,70200000
FRP 1,25,90000000

Two columns is the whole input. Jump to the details, including the four things that quietly ruin a lot fit and how the tool checks for each one.

Learning curve forecast and Monte Carlo cost risk

Real output. cost_core fits an 85% Wright learning curve and forecasts future lots (left), then runs a 10,000 iteration Monte Carlo total cost simulation with P50/P80/P90 thresholds (right). Regenerate with python make_hero.py, which reads a local data.csv that isn't committed, since .gitignore excludes *.csv.

No real or proprietary data is committed to this repository. You supply your own for fit-lots, and nothing you pass in gets stored here. Everything the repo ships with, and everything the test suite runs on, comes from a seeded generator producing invented programs in the shape of CADE submissions.

What it does

Module Purpose
cost_core.lotmodel The lot cost engine. Analogy lots in, estimate lots out. Fits LC / Rate / LC+Rate, selects on significance with an AICc tiebreak, and layers refits, influence, prediction intervals and buy risk on top. Carried over from the desktop tool in lot-cost-model, which runs on this code from its 3.0.0 rather than a copy of it
cost_core.program WBS roll-up. Several elements, fitted, factor or amount, priced against one lot schedule and correlated into a programme estimate
cost_core.lots Your own data. Units and cost per lot, in CSV or Excel. Runs the same three model engine, then layers the statistics on top
cost_core.synth Seeded synthetic CSDR/SRDR generator: DD 1921, DD 1921-1, DD 1921-2, Cost and Hour Report (FlexFile), Quantity Data Report, SRDR (DD 2630), with realistic pathologies to clean
cost_core.ingest ETL to one normalized long table: WBS crosswalk, base year normalization, resubmission dedup, loud validation gates, row level provenance
cost_core.fitting Shared estimator: OLS, MUPE and ZMPE, with delta method prediction and confidence intervals
cost_core.learning_curve Wright (cumulative average) and Crawford (unit) theories, rate breaks, prediction intervals
cost_core.cer Parametric CERs, log log and linear, with leverage and influence diagnostics, extrapolation warnings, small sample guardrails
cost_core.monte_carlo Correlated WBS level risk: Gaussian copula or Iman Conover, PSD repair, discrete risks, tornado, convergence
cost_core.reporting S curve, tornado, cost improvement curve, CER diagnostics, and the assumptions log

Installation

Python 3.9 or higher. The distribution name is cost-core and the import name is cost_core. Nothing is published yet, so install from a clone:

python -m venv .venv && .venv/Scripts/activate && pip install -e ".[plots]"

That pulls in pandas, numpy, scipy and openpyxl, plus matplotlib for the [plots] extra. Excel input and the workbooks the lot engine writes both need openpyxl, so it's installed by default rather than as an extra. matplotlib is the other way round: it draws the charts and nothing else, so installing without the extra gives you the whole engine, the CERs, the risk simulation and the Excel workbooks, and anything that draws a PNG tells you to add [plots] when you reach it.

numpy and scipy also carry an upper bound, numpy<2.5 and scipy<1.18. That is about reproducing numbers, not about running: above those versions the library imports and works, but it stops reproducing the figures this release pins. Both land the unbiased MUPE and ZMPE refits in a slightly different place, which moves golden leaves by up to 8e-9 and 4.5e-8 relative against a 1e-9 tolerance, and scipy 1.18 also changes the simulated draws, which the frozen-draw tests hash. Both are seen on Windows, where the goldens were captured. Elsewhere those refit leaves carry a measured allowance for the machine's own last bits and the hashes are not checked, so a bound has to be measured on Windows. Raising a bound means rebaselining the goldens and the frozen draws against the new stack and writing down what moved, the same process every other rebaseline here went through. CHANGELOG.md carries the measurements.

Quick start

With your own data

ce-core fit-lots --csv mylots.csv --dollar-year 2026 --forecast "30,40" --out results/

Costs come back in the units they went in, because the fit is scale free and this command converts nothing. Headings that read ($K) are the engine's own column names, carried over from the desktop tool whose input column is AUC ($K), and the S-curve formats the same numbers as dollars, so read both as "cost" in your file's units unless that file really is in thousands.

Prints the fitted slope and first unit cost, the standard error and CV, an interval on the slope, and a per lot percentage error showing which lots the curve misses. With --out it also writes those tables as CSV and an ASSUMPTIONS.md; the one chart this command draws is the buy S-curve, and that needs --simulate. See Fitting a curve to your own lot data.

With generated data, to see the whole pipeline

Generate synthetic submissions, ingest and normalize them, fit a learning curve and a CER, simulate with correlation, then write charts, tables and an assumptions log:

ce-core full-run --out artifacts/ --seed 7

That writes:

artifacts/
  ASSUMPTIONS.md              the written assumptions and provenance log
  charts/                     s_curve, tornado, learning_curve,
                              cer_diagnostics, summary_table  (PNG, 200 dpi)
  tables/                     every table behind those charts, as CSV
  artifacts/source_reports/   the six synthetic submissions
  artifacts/wbs_crosswalk.csv the persisted name crosswalk
  artifacts/inflation_index.csv

The same seed reproduces the run exactly.

The lot cost engine

The lot cost engine is cost_core.lotmodel, the code the desktop tool runs on. Historical analogy lots (fiscal year, quantity, unit cost) are the history; estimate lots (fiscal year, quantity, complexity factor) are the buy being priced.

from cost_core.lotmodel import run_lot_cost_model, generate_analyst_summary, enrich_run

projections, ctx = run_lot_cost_model(analogy_df, estimate_df)
summary = generate_analyst_summary(ctx, {"Program": "TEST"})
extras = enrich_run(ctx, projections, summary)

Three models get fitted to the analogy lots, and every estimate lot is priced under all three, so the projections carry the models the tool didn't pick right alongside the one it did:

LC        ln(cost) = ln(T1) + b*ln(lot midpoint)
Rate      ln(cost) = ln(T1) + c*ln(lot quantity)
LC+Rate   both terms together

Selection goes to LC+Rate when its rate coefficient is significant, to Rate when the rate slope is significant and beats LC by more than the AICc tie threshold, and to LC otherwise. Where AICc disagrees with the significance gate, the summary says so instead of hiding it. Because the lot midpoint depends on the slope you're fitting, the fit iterates to a fixed point. That's the Goal Seek the original workbook did by hand.

What the statistics layer adds

The estimate itself is untouched by any of this. A golden master test fails if a single coefficient moves. What enrich_run reports is how much confidence those numbers can carry.

Retransformation bias. The fit is OLS on ln(cost), then exponentiated back. That estimates the median and understates the mean by exp(s²/2). MUPE and ZMPE refit the same regressors under a proportional error loss and drive the mean percentage error to zero, so the bias gets measured on your data instead of argued about.

Influence. Six analogy lots is a normal sample here, and at that size one lot can set the slope while every summary statistic still looks healthy. Leverage and Cook's distance name it. On the reference programme in the tests, analogy lot 1 carries leverage 0.77 and Cook's D 4.00.

Prediction intervals on every projected lot. For a new lot, carrying the residual scatter, with a t multiplier because sigma is estimated rather than known.

Buy risk. A distribution over the total of the estimate lots with P50/P80/P90 and where the point estimate falls on it. Residuals across lots are correlated at 0.30 by default, for the same reason WBS elements are.

enrich_run returns the four tables (Fit_Methods, Influence, Prediction_Intervals, Buy_Risk) as frames. The desktop tool writes the risk ones into its workbook after the original three sheets, so an analyst who wants only the original three still gets exactly those.

Fitting a curve to your own lot data

The simplest way in. Two columns, one row per lot:

lot,units,cost
LRIP 1,22,96800000
LRIP 2,18,70200000
FRP 1,25,90000000
FRP 2,30,100500000
ce-core fit-lots --csv mylots.csv --dollar-year 2026 --forecast "30,40" --out results/

The lot column is optional. Common header spellings (Qty, Quantity, Total Cost, Amount, and so on) are recognized automatically, and anything unusual you name with --units-col / --cost-col. Currency formatting like $1,200,000 parses fine. .xlsx works out of the box.

Everything else is derived. Lot 1 is units 1 to 22, lot 2 is units 23 to 40, and so on by running total. That's what turns a flat list of lots into positions on a curve. You get the fitted slope and first unit cost, standard error and CV, an interval on the slope, a per lot percentage error showing which lots the curve misses, prediction intervals on forecast lots, and an ASSUMPTIONS.md.

Re-using the curve on another program

The fit is also an estimating relationship you can lift and apply somewhere else. The equation gets printed and written to equation.csv. The four lots above select LC+Rate, so theirs carries both terms:

Unit Cost = 7,424,501.70 * midpoint^(-0.108355) * qty^(-0.093348)

Where only the learning term survives the significance gate the qty factor is absent. Drop FRP 2 from that sample and the remaining three lots select LC, printing Unit Cost = 5,586,219.86 * midpoint^(-0.108548). The priced lots carry whichever terms the equation does. There's a test for exactly that, because for a while they didn't.

--price-lots applies the selected model to any buy profile from unit 1, producing the learning curve table an analyst would build by hand:

ce-core fit-lots --csv mylots.csv --dollar-year 2026 --price-lots "10,15,20,25,30" --out results/

The lot_midpoint column is the algebraic midpoint, meaning the unit whose cost equals the lot average. Most tools approximate it, because they only have an approximate lot average to work from. Here the lot average is exact, so the midpoint gets solved for directly. There's a test asserting the cost at the midpoint equals the lot average, which is its definition.

This is the analogy use case: price a program with no cost history of its own using the slope from one that does. Whether that's valid is a judgement, not a result. The slope carries across only if the two programs are comparable in product, process, rate and contractor. Nothing in the data can confirm that, so the assumptions log records it as an untested assumption and notes that the extra error it introduces isn't in any interval reported.

Forecasting the next buy, with risk

--forecast prices future lots continuing from the last unit built, with prediction intervals. --simulate then Monte Carlos them:

ce-core fit-lots --csv mylots.csv --dollar-year 2026 --forecast "30,40" --simulate 50000 --out results/

Unlike the WBS level simulator, this needs no elicited distributions. The uncertainty is measured from the program's own history. Two sources get propagated: parameter uncertainty in the fitted slope and T1, which dominates on a short series, and lot to lot scatter, which is what makes the answer a prediction about a real lot. Residuals across future lots are correlated at 0.30 by default for the same reason WBS elements are. Consecutive lots share a workforce and a schedule, and treating them as independent understates the spread of the whole buy.

It does not include schedule risk, requirement changes, or rate changes the history never saw. That's a narrower claim than a full risk model, and the log says so.

How it fits

Three candidate models against the lot midpoint, one selected:

LC        ln(unit cost) = ln(T1) + b*ln(lot midpoint)
Rate      ln(unit cost) = ln(T1) + c*ln(lot quantity)
LC+Rate   both terms together

This engine came across from the desktop tool in lot-cost-model, which since its 3.0.0 imports it rather than keeping a copy, so the two are the same code by import rather than by descent. All three models get fitted and all three price every lot, so the alternatives stay on the record. Because the midpoint depends on the slope you're fitting, the fit iterates to a fixed point.

Four things that quietly ruin a lot fit

The tool checks all four, because each one leaves a fit that looks perfectly healthy while the slope is several points wrong.

Nonrecurring cost in the totals. Nonrecurring is front loaded, so including it makes early lots look expensive and the curve reads steeper than the production process really is. That overstates future savings. --cost-basis has to be declared, and total warns.

Escalation left in "constant" dollars. --dollar-year is required. No index gets applied, but constant dollars are constant relative to a year, and an output nobody can place in a year can't be escalated or compared. The tool checks whether cumulative average cost ever rises, which can't happen on a learning curve, and it also tests the log log residuals for a bend.

Be careful about what that second check can actually do. Fitted against the lot midpoint it's a poor escalation detector: the fitted slope moves with the escalation, the midpoint moves with the slope, and the trend gets absorbed instead of being left in the residuals. It catches a rate break, a design change or a production gap, not moderate escalation. The level check needs roughly 10% a year before it bites. Below that, moderate escalation and genuinely slower learning can't be told apart without a fiscal year attached to each lot, and the log says exactly that. There's a parametrized test at 2%, 4% and 6% asserting the miss, so the limit is documented rather than discovered later.

Lots that don't start at unit 1. If the program has a prior buy the curve has already learned through, --first-unit shifts the series. Otherwise the fitted first unit cost describes a unit nobody built.

Too few lots. Degrees of freedom are lots minus 2. Two lots interpolate exactly and are refused. Three gives one degree of freedom and an interval too wide to support a decision. Five is the practical floor. The tool fits below that but says so loudly.

Usage guide

Full run

ce-core full-run --out artifacts/ --seed 7 --iters 50000 --theory crawford --method mupe --correlation 0.30

--clean generates data with no reporting pathologies, which is how the tests show that the pipeline recovers the generating truth exactly.

Fit a learning curve

Takes a CSV with program, lot, unit_quantity and unit_cost columns.

ce-core fit-curve --csv your_history.csv --out model_params.json

Forecast future lots

ce-core forecast --model model_params.json --quantities "32,64,128" --out forecast.csv

Run a Monte Carlo simulation

In PowerShell, wrap the JSON arguments in single quotes.

ce-core simulate --n-iter 10000 --unit-cost-dist '{"type": "lognormal", "mean": 5.0, "sigma": 0.2}' --quantity-dist '{"type": "triangular", "left": 40, "mode": 50, "right": 75}' --out sim_results.csv

As a library

from cost_core.synth import generate_program
from cost_core.ingest import normalize_program
from cost_core.learning_curve import Theory, fit_from_progress_report

program = generate_program(seed=7)
data = normalize_program(program)                    # raises if a gate fails
curve = fit_from_progress_report(
    data.learning_curve_input(), theory=Theory.CRAWFORD, method="mupe"
)
curve.forecast_lots([[109, 132]], level=0.80, kind="prediction")

Methodological choices

Why MUPE and ZMPE, not just OLS

The standard cost fit is ordinary least squares in log space, followed by exponentiating back to dollars. That retransformation is biased. If the log space errors are normal with variance , then

E[y | x] = f(x) · exp(s² / 2)

so the retransformed value estimates the median and understates the mean by a factor of exp(s²/2). On a 30% CV relationship that's roughly a 4 to 5% understatement baked into the estimate before any risk analysis even starts, and it runs in the direction that makes a program look cheaper.

Two unbiased alternatives are provided, and both drive the mean percentage error to exactly zero:

MUPE (minimum unbiased percentage error) minimizes Σ (y - f)² / f_prev² by iteratively reweighted least squares. At its fixed point, the normal equation for a multiplicative scale parameter collapses to Σ (y - f)/f = 0.

ZMPE (zero percentage bias minimum percentage error) minimizes Σ ((y - f)/f)² subject to Σ (y - f)/f = 0. Same zero bias property, but imposed as a constraint rather than emerging from the algebra, and it gives a different slope.

retransformation_bias() measures the bias three ways: the theoretical exp(s²/2), Duan's nonparametric smearing estimate, and the observed shift against MUPE and ZMPE. So the correction gets quantified instead of asserted.

Prediction intervals, not confidence intervals

These aren't interchangeable, and the confusion always runs the same direction.

A confidence interval covers the mean response at a point, meaning where the fitted line is. It shrinks toward zero as the sample grows.

A prediction interval covers a single new observation, meaning where the next actual program will land. It carries the residual scatter as well as the parameter uncertainty.

The variance relationship is exact:

Var_prediction = Var_confidence + σ²

That extra σ² is the spread of programs about the line, and no amount of additional data removes it. A cost estimate forecasts one new program, so the prediction interval is the correct one. CER.predict() takes kind explicitly and defaults to "prediction".

Why correlation matters

Sampling WBS elements independently is the spreadsheet default and close to the worst assumption available. Elements on one program share a workforce, a management chain, a supply base and a schedule. When one runs late they mostly all run late. The variance of a sum is

Var(Σ Xᵢ) = Σ Var(Xᵢ) + 2 · Σ_{i<j} ρᵢⱼ · sdᵢ · sdⱼ

so for k equally variable elements at a common ρ, ignoring correlation understates the variance of the total by exactly 1 + ρ(k-1). Ten elements at ρ = 0.3 is a factor of 3.7 in variance, close to a doubling of the standard deviation, and it lands on the upper tail, which is where the P80 lives.

Because independence is so rarely right, RiskModel applies a non zero default correlation when none is supplied, and warns that it did so. A default is an assumption, and an unstated assumption is the failure this documentation exists to prevent. correlation_impact() reports the measured and the closed form inflation side by side, so the claim doesn't rest on the simulation alone.

Standard error and CV, not R²

R² measures how tightly points hug the fitted line, which a wrong model can do perfectly well. In tests/test_learning_curve.py, data generated under Crawford unit theory and fitted as a Wright curve returns R² of about 0.996 with a demonstrably wrong forecast. Standard error is in dollars and CV is a proportion. Both are arguable. R² is reported, but last.

Projections have to satisfy the equation

The original desktop tool priced its Rate lots on the lot midpoint, which isn't the variable that model regresses on, and priced LC+Rate without the rate factor at all. So it printed an equation and then printed lot costs that didn't satisfy it. On example_lots.csv that overstates a back cast of the fitted lots by 36% against a known total, while the residual columns from the same run showed the model tracking those lots to about 1%. One run, two formulas.

Dropping the term isn't a modeling choice you could defend. It evaluates the fit at a lot quantity of one unit while keeping the learning position of the real lot, and because the rate exponent is negative it only ever biases upward.

The corrected behavior is the default. LegacyRateOmission reproduces the old numbers for anyone who has to match a legacy workbook, and the command line has --legacy-rate-omission for it, which prints a warning when you use it. Passing the old setting name raises instead of being ignored, because a caller who asked for legacy behavior and quietly got something else is worse off than one who gets an error.

The guard is a test that retypes the printed equation and evaluates it against the printed projections, for all three models. That test is why this is a paragraph about a fix rather than a known issue.

Wright and Crawford are different theories

Wright's cumulative average form says the average cost of the first x units follows T1·x^b. Crawford's unit form says the cost of unit x does. Which one applies is a property of the production process, not a modeling preference. fit_curve() makes the caller choose and compare_theories() reports both, because the same data under the two gives materially different forecasts.

Mapping to the GAO Cost Estimating and Assessment Guide

Every run emits an ASSUMPTIONS.md organized around the four characteristics of a reliable estimate.

Characteristic How this library addresses it
Comprehensive All six report shapes ingested. Recurring and nonrecurring cost, five functional categories and discrete risks all modeled. The WBS crosswalk surfaces unmatched elements rather than dropping them
Well-documented Row level provenance from every output number back to its source submission. The crosswalk and inflation index are persisted artifacts, not inline logic. The assumptions log separates what was measured from what was assumed, and counts the assumptions
Accurate Validation gates reconcile row counts and dollar totals within and across reports, and fail the run when they disagree. Retransformation bias is measured and corrected. Estimating methods are compared rather than assumed
Credible Prediction intervals on every forecast. Leverage and influence diagnostics on the CER. Extrapolation flagged, including hidden extrapolation. The correlation assumption's effect quantified against independence. P80 convergence checked

Project structure

cost_core/
  fitting.py          shared OLS / MUPE / ZMPE estimator and intervals
  lots.py             your own lot data: units and cost per lot
  cli.py              the ce-core command line interface
  lotmodel/           the lot cost engine: LC / Rate / LC+Rate on lot midpoints, summary, enrichment
  program/            WBS roll-up of several elements into one programme
  learning_curve.py   Wright and Crawford theories, rate breaks
  monte_carlo.py      correlated risk simulation
  data_io.py          CSV and SQLite loading
  synth/              synthetic CSDR/SRDR generator
  ingest/             crosswalk, inflation, normalization pipeline
  cer/                parametric CERs and diagnostics
  reporting/          charts, assumptions log, the Excel workbooks, end to end run
tests/                property tests, see below

Tests

pip install -e ".[plots]" pytest
pytest tests/ -q

requirements.txt also exists, but it holds the exact versions this repo develops against, and four of the five will not install on 3.9, three of them not on 3.10. It is the pinned development set, not the way to set up a checkout on an older interpreter. Install the package and let the version range in pyproject.toml resolve.

759 tests, run on Python 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on every push, which is the whole supported range; four of them, the frozen-draw hashes, run on Windows only. They assert mathematics against closed form answers rather than against recorded output, with one deliberate exception: tests/goldens pins what the lot engine produced on 6 September 2026, so a refactor that moves a number has to say so. The strongest ones:

Our OLS is the textbook OLS. The generic estimator reproduces scipy.stats.linregress and the normal equations to machine precision, and the delta method prediction interval reduces algebraically to s·√(1 + 1/n + (x₀-x̄)²/Sxx).

MUPE and ZMPE drive the mean percentage error to exactly zero. That's what their names mean, and it's asserted to 1e-9. ZMPE's sum of squared percentage errors is also proven to be no larger than MUPE's, which is a theorem, not a tuning outcome.

Cook's distance is checked against an actual leave one out refit. The closed form is exact, so the test drops each program, refits, and confirms the formula reproduces the movement in the fitted surface.

Variance inflation is exactly 1 + ρ(k-1). Asserted across element counts and correlations, and confirmed against simulation.

Tornado variance shares sum to exactly one, because the covariance decomposition Var(T) = Σ Cov(Xᵢ, T) is an identity when T is the sum.

A messy program normalizes back to the generating truth to the cent. Name drift, mixed then year and base year dollars, resubmitted periods and a mid program quantity change are all reversible by construction, so the pipeline that reverses them has no excuse for landing anywhere else.

Learning curve identities are definitional. Doubling quantity multiplies the right quantity by the slope under each theory. Wright's unit costs telescope back to its cumulative total. Crawford's lot cost is the exact sum of its units.

Simulations are seed deterministic. A P80 that moves between runs isn't a number you can put in front of anyone.

Limits get tested, not just capabilities. There's a parametrized test asserting that both escalation checks stay silent at 2%, 4% and 6% a year under a midpoint fit, while the fitted slope drifts several points off the truth. A second test confirms the level check does catch 15%. Documenting where a diagnostic stops working matters more than showing where it works.

The projections satisfy the equation the tool prints. Retyped by hand for all three models and evaluated against the projected costs, to the cent the column is rounded to. Flipping the legacy switch back on fails five of these, which is how I know they'd have caught the original defect.

Bad input is refused, not absorbed. Zero degrees of freedom, a missing base year, two lots, fractional units, unmatched WBS names, non positive costs in a log fit, a correlation matrix that isn't symmetric, a rate break beyond the data, an unknown interval kind, an index asked for a year it doesn't cover. Each one raises instead of producing a plausible looking number.

Release files for cost-core 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cost-core 1.0.1
File Size Uploaded
cost_core-1.0.1.tar.gz 1.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for cost-core 1.0.1
File Interpreter ABI Platform
cost_core-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.8 MB

Release files / cost_core-1.0.1.tar.gz

Download URL cost_core-1.0.1.tar.gz
Size 1.5 MB
Tags Source
SHA-256 checksum
How to use checksums
f83e51178194f8cd12887f68ca0e31a1be4c9d2cd5c711f52f9bd254f1585925
BLAKE2b-256 checksum
How to use checksums
46535473ffa62f888db80071b3adf6535441122ff9f543de0e058ce33f35a3b9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / cost_core-1.0.1-py3-none-any.whl

Download URL cost_core-1.0.1-py3-none-any.whl
Size 206.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d791cf94e1d05e7306cf354253a3d4dd2d46be3c65f8a4f56018f21a52ed3d6c
BLAKE2b-256 checksum
How to use checksums
39042b08330c72bed01762e1a557fd5a84b10c67df9403cb216b60ea013a7e90
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page