Stability and versioning¶
If you call this library from a service, you need to know which names you can import safely, what the stored decision record promises, and which upgrades might break you. This page is that contract. It lists the public names grouped by task, the record format, and the rules for what counts as a breaking change. It assumes you have read Your first decision.
Current version: 0.1.0 (grounded-decision --version), Apache-2.0,
Python 3.12 or newer. The project follows
semantic versioning; every change is in the
changelog.
The web showcase's session protocol (messages between a front end and the engine) has its own semantic version and compatibility rules, checked by a test: see Session protocol.
What is public¶
Public means: the names in grounded_decision.__all__, the four
functions in grounded_decision.examples.catalog, the
grounded-decision command, and the record format below. Everything
else can change in any release.
Names you can import from grounded_decision¶
This is the complete __all__ (42 names), grouped by what you use them
for.
| Task | Names |
|---|---|
| Write a policy | parse, evt, act, seq, star, union, Statechart |
| Check a policy | compile_expr, star_height, StarHeightError |
| Build and run a decision | Module, Event, EvidenceSnapshot, DecisionRecord, Verdict, Envelope |
| Build evidence | snapshot_from_graph, reconstruction_report, threshold_report |
| Combine policies | Pipeline, compose_parent |
| Serve, replay and simulate | handle_request, DecisionSession, EvidencePrior, Volatility, draw_snapshot |
| Learn settings from history | learn_parameters, RiskTarget, HistoricalCase, LearnedParameters, RiskMonitor |
| Change caps and defaults | Limits, DEFAULT_LIMITS |
| Hard side questions (router) | Subgoal, dispatch |
| Side analyses (compare) | compare_outcome_laws, sample_two_row, best_coordinate, single_item_one_sample, single_item_max_of_samples |
| Published constants | gw_alpha, k_median_threshold |
Also available: grounded_decision.__version__ (read from the
installed package metadata; 0.0.0+unknown when running from a source
tree that was never installed) and grounded_decision.main (the
command-line entry point).
Signatures and docstrings for each: API reference.
Other supported imports¶
grounded_decision.examples.catalog:list_examples,run_example,run_all,interface_map.- Types that appear inside public return values, imported from their
submodule to read or construct them:
ChannelReport,Health,CatalogRef,ChannelName,Trust(grounded_decision.types),RouterResult(grounded_decision.router.dispatch). Their fields may grow in a minor release. ParseErrorandCompileErrorfromgrounded_decision.grammar, so you can catch them.- Everything in
grounded_decision.learn.__all__(grid, risk measures, monitor status, exact binomial statistics,case_arraysandpredicted_acts).
Not public¶
- Any name starting with
_, includingModule._state(useModule.stateandModule.restore). grounded_decision.examples.commonand the individual example modules, includingreference_service.py. Copy them; do not import them from production code.- Submodule functions not listed above. They are documented in the API pages for contributors and can move.
The decision record format¶
DecisionRecord.to_dict() is the format to store and send between
services; DecisionRecord.from_dict() reads it back. Event,
EvidenceSnapshot, Health, ChannelReport and CatalogRef have the
same pair. handle_request(module, body) accepts
{"state": ..., "event": {...}, "evidence": {...}} and returns the
same dictionary.
A real record, the held third patient from
uv run grounded-decision examples clinical_triage --json, with each
check trimmed to four fields and health shown in full:
{
"verdict": "abstain",
"action": null,
"proposed_action": "discharge",
"reason": "d λ² = 0.0000 vs 1+0.050 (at or below KS; abstain); evidence density inside operational window (theorem width=3.26e+05, vacuous=True)",
"module": "clinical_triage",
"event": {"name": "low_risk", "payload": {"heart_rate_bpm": 88, "spo2_percent": 95}},
"channels": [
{"name": "certificate", "passed": true, "trust": "lean", "summary": "star-height 0 ≤ 3; 4 states ≤ 256"},
{"name": "game", "passed": true, "trust": "lean", "summary": "parity_win=True nonnegative_mean=True"},
{"name": "reconstruction", "passed": false, "trust": "paper", "summary": "d λ² = 0.0000 vs 1+0.050 (at or below KS; abstain)"},
{"name": "threshold", "passed": false, "trust": "operational", "summary": "evidence density inside operational window (theorem width=3.26e+05, vacuous=True)"},
{"name": "conformal", "passed": true, "trust": "operational", "summary": "conformal p=1.000 vs α=0.100 (cover)"},
{"name": "support", "passed": true, "trust": "operational", "summary": "support 0.80 vs minimum 0.50 (supports the proposed action)"}
],
"health": {
"d_lambda_sq": 0.0,
"reconstruction_margin": 0.05,
"p_hat": 0.4,
"p_threshold": 0.15,
"threshold_width_theorem": 325758.4501703898,
"threshold_width_operational": 1.2426698691192237,
"in_theorem_window": false,
"in_operational_window": true,
"theorem_window_vacuous": true,
"conformal_p": 1.0,
"star_height": 0,
"game_solved": true,
"mixer_status": "idle",
"support": 0.8
},
"envelope": "p_exact",
"extras": {"state": "auto"}
}
What the format promises in 0.x:
- The top-level keys above are always present.
channelsalways has six entries, in this order:certificate,game,reconstruction,threshold,conformal,support.from_dictreads records and evidence written before thesupportcheck existed (five channels, nosupportkey inhealthor the evidence) without error; the missing values read asNone.verdictis one ofact,abstain,degrade;trustis one oflean,paper,operational;envelopeis one ofp_exact,fpras,ugc_opt,undec,exptime_trap.extras["state"]is the state to save. Treat any otherextraskeys as opaque.- The wording of
reasonandsummarymay change in any release. Do not parse them; usepassed,metricsandhealth. metricsandhealthmay gain keys. Do not assumemixer_statusstays"idle".
Field meanings: The decision record.
Versioning policy¶
The project follows semantic versioning:
- Before 1.0 (now), a minor release (0.1 → 0.2) may change behaviour, including the breaking changes in the table below. Every such change is listed in the changelog under "Changed" or "Removed", with migration notes saying what to do. A patch release (0.1.0 → 0.1.1) only fixes bugs.
- From 1.0, a breaking change bumps the major version.
- Renamed public names keep the old name as an alias for one minor release when that is cheap to do, and the changelog says so. When it is not (for example a changed record format), the migration note says how to convert.
What counts as a breaking change¶
| Change | Breaking? |
|---|---|
Removing or renaming a name in __all__ or examples.catalog |
Yes |
Adding, removing or reordering a check in channels |
Yes |
| Rejecting a policy that used to compile (for example an ambiguous one) | Yes |
Removing or renaming a Verdict, Envelope, ChannelName or Trust value |
Yes |
Removing a top-level key from to_dict() |
Yes |
Making a default in Limits stricter (lower cap, higher margin, higher α) |
Yes: decisions that used to act may now hold |
| A policy text that used to parse now failing, or parsing to a different policy | Yes |
| Renumbering the states a given policy text compiles to | Yes: saved states would change meaning |
Adding a name to __all__, a key to metrics, health or to_dict() |
No |
Making a default in Limits looser |
No |
| Adding an example id, or a router subgoal | No |
Changing the wording of reason, summary or printed reports |
No |
| Rejecting input that was previously accepted by mistake (a bug fix) | No, but listed under "Fixed" |
Upgrading safely¶
- Read the changelog entries between your version and the new one.
- Replay recorded events through the new version and compare verdicts with the old one before switching traffic (Roll out safely).
- If you store states, compare policy fingerprints after the upgrade; the reference service refuses mismatched states with HTTP 409 (State and policy versions).
Machine-readable surface¶
uv run grounded-decision interfaces prints interface_map(): the
decide inputs, verdicts, the six checks, builders, router subgoals,
compare helpers and every example id. Use it when another tool needs
the surface without importing the package. Output and key list:
Command line.
Typing¶
The package ships py.typed, so type checkers use its annotations.
The types are frozen dataclasses; there is no runtime validation
layer, so check inputs at your service boundary.