| name | generate-bounds |
| description | Use when the user wants to generate low/base/high assumption ranges (bounds) for missing or uncertain variables in a validated extract-parameters-from-full JSON, in preparation for deterministic scenarios or Monte Carlo |
Generate Bounds for Extracted Parameters
Overview
Wraps the bounds-estimator system prompt at system-prompt.txt (next to this file) and applies it to a parameter JSON produced by extract-parameters-from-full (and ideally already passed validate-parameters). Output is a strict JSON object keyed by variable id, with low / base / high / unit / rationale / source for every variable that needs an assumption range.
Stage 4 of the pipeline described in planexe_simulator/README.md.
When to Use
- User asks to "generate bounds", "estimate ranges", "add low/base/high", or "prepare for scenarios" given an extract-parameters-from-full JSON
- User wants to fill in assumptions for
missing_values_to_estimate and uncertain key_values before running deterministic scenarios or Monte Carlo
- Pipeline step between
validate-parameters (passes clean) and generate-calculations / run-scenarios
Not for: regenerating the parameter JSON (use extract-parameters-from-full), validating the JSON (use validate-parameters), or producing Python code (use generate-calculations).
Workflow
- Get the input JSON path. If the user did not provide one, ask. Do not guess.
- Read
system-prompt.txt (sibling of this SKILL.md). Its selection rules and spread heuristics are authoritative.
- Read the parameter JSON. Assume it has already passed
validate-parameters; if it visibly hasn't, tell the user and offer to validate first.
- Produce the bounds JSON per the system prompt.
- Self-audit before output. For each bound entry, verify:
- Every numbered citation in
rationale (Risk N, Issue N, Decision N) refers to content that substantively supports the claim — not just lexically present.
- For variables feeding a declared gate threshold, the base-vs-threshold relationship is consistent with the rationale. If base implies base-case gate failure, the rationale names a report-internal anchor that justifies the shift.
source: "data" entries name an explicit anchor in the rationale.
- For asymmetric bounds, the rationale notes the asymmetry's source.
If any check fails, revise the bound; do not ship inconsistent state.
- Output destination. Default: print the JSON to chat. If the user asks for a file, write to the path they specify. Suggested default file path:
<input-basename>.bounds.json next to the input.
Selection Rules (re-stated for emphasis — see system prompt for full detail)
Generate one bounds entry for every id that meets ANY of:
- it appears in
missing_values_to_estimate
- it is a
key_value with value_type ∈ {inferred, missing_but_needed}
- it is a
key_value with value == null
- it is a
key_value with uncertainty == high
- it is a
key_value with uncertainty == medium AND modelling_priority ∈ {critical, high}
Skip: explicit/derived key_values with low uncertainty, derived_questions, recommended_first_calculations, and formula LHS-only outputs that are not declared as inputs.
Spread by Uncertainty
| Uncertainty | Spread around base |
|---|
| low | ±10–20% |
| medium | ±25–50% |
| high | ≥±50%, up to a 2–5× factor when genuinely speculative |
Always low ≤ base ≤ high. Fractions stay in [0, 1]. Counts of discrete things (people, kits, centers, months) use integer bounds.
Output Shape
{
"<variable_id>": {
"unit": "fraction",
"low": 0.10,
"base": 0.20,
"high": 0.30,
"rationale": "Short, ≤50 words. Include required disclosures when applicable.",
"source": "data" | "assumption",
"sampling_discipline": "fraction",
"non_negative": true,
"default_pass_probability": null
}
}
Top-level is a single object keyed by variable id. Order keys roughly by importance (critical → high → medium → remaining missing values). Use "source": "data" only when the range is anchored in a citable real-world reference for similar programs; otherwise "assumption".
Sampling-discipline tag
Each entry MUST carry a sampling_discipline chosen by the LLM based on the variable's nature. Downstream consumers (run-scenarios, monte-carlo runner) read this tag directly and do not pattern-match on unit strings — so the LLM is the single authority for this classification.
| Value | Meaning | Downstream behavior |
|---|
"fixed" | low == base == high; genuinely pinned | Always return that value |
"bernoulli_gate" | Binary pass/fail (staged funding, permit toggle, regulatory pass, conditional release). Requires default_pass_probability ∈ [0, 1] | Bernoulli draw; low on fail, high on pass |
"integer" | Countable units — people, households, sites, kits, days, … | Sample continuously, round, re-clamp to [low, high] |
"fraction" | Bounded fraction in [0, 1] | Clamp draws to [0, 1] |
"continuous" | Real-valued; no rounding, no extra clamping beyond [low, high] | Plain triangular/uniform draw |
non_negative is independent of discipline: set true when the variable cannot legitimately go below zero (continuous monetary amounts, counts, capacities); set false only when negative values are physically meaningful (a delta or net change variable).
default_pass_probability is required (a number in [0, 1]) when sampling_discipline == "bernoulli_gate" and MUST be null for every other discipline.
If no variable needs bounds, return {}.
Common Mistakes
| Mistake | Fix |
|---|
| Bounding every variable, including known explicit facts | Skip explicit/derived key_values with low uncertainty — they are facts, not assumptions |
Returning low == base == high for variables with real uncertainty | Give a real range; identical bounds are only for genuinely pinned KPI commitments |
Wrapping output in ```json fences | Raw JSON only |
Including derived_questions or recommended_first_calculations ids | Those are outputs/questions, not bounded inputs |
| Inventing ids that are not declared in the parameter JSON | Every key must correspond to a declared id in key_values or missing_values_to_estimate |
Picking fractions outside [0, 1] (e.g. base 1.5 for a rate) | Clamp to the unit's natural range |
Ignoring the parameter's uncertainty and giving everything the same ±20% spread | Anchor the spread on the parameter's stated uncertainty level |
| Rationale cites a numbered artifact (Risk N, Issue N, Decision N) that does not substantively support the claim | Re-read the cited artifact. The number must match content, not just exist in the report. If you cannot find a substantively correct citation, drop the citation and mark source: "assumption". |
Shifting actual_X base past the X_target threshold without a report-internal anchor | Center base at the committed value unless a named Risk / Issue / Decision / premortem / expert-criticism passage forecasts a gap between commitment and reality. "Realistic execution" and "operational drift" are not anchors. |
Generating bounds for a threshold or target variable (ids ending in _threshold, _target, _ceiling, _floor, _limit) | Thresholds enter the simulation as their stated single value. Randomising them silently changes what pass_rate measures and breaks the threshold_basis = report_explicit contract. Skip these variables. |
Triangular bounds with base = high = target on a >= target gate variable (ring-fenced reserve, segregated escrow, allocation share, KPI floor that physically cannot overshoot) | The continuous triangular CDF has zero probability mass at the exact peak — the >= 0 margin gate fails ~100% of runs even when the commitment is intact. The Critical verdict you report is a property of the bound shape, not the plan. Switch to sampling_discipline: "bernoulli_gate" with low = shortfall floor (e.g. 0 or partial-segregation amount), high = the target, and default_pass_probability = realistic execution probability of the commitment (e.g. 0.95 for a signed escrow, 0.75 for a sponsor-committed reserve, 0.5 for a contested allocation). Bernoulli returns high on pass and low on fail, so the gate cleanly tests whether the commitment was executed. |
Reference
- System prompt (authoritative):
system-prompt.txt
- Pipeline overview and "what needs bounds" list:
../../README.md, Stage 4
- Companion skills:
../extract-parameters-from-full/SKILL.md, ../validate-parameters/SKILL.md
- Example input for testing:
/tmp/extract-params-heatwave-v8.json (passes validate-parameters with valid: true)