Skip to content

Stop the data skill steering agents to microimpute and uk-data - #108

Merged
MaxGhenis merged 5 commits into
mainfrom
data-skill-old-stack
Nov 8, 2026
Merged

MaxGhenis merged 5 commits into
mainfrom
data-skill-old-stack

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

The policyengine-data skill steered agents to the old data stack. PolicyEngine archived microimpute on 2026-11-08 (PolicyEngine/microimpute#224), and Microcosm imputes PolicyEngine's microdata with microcosm-fit. This PR corrects skills/policyengine-data/SKILL.md to match.

  • UK paragraph (lines 164-167 on main). It said policyengine-uk-data's Enhanced FRS "feeds the UK Microcosm build" and that UK imputations "land" there. The new text says that:
    • uk-data builds the Enhanced FRS and still imputes with microimpute 1.8.1;
    • policyengine.py defaults UK simulations to Microcosm's microcosm_uk_2024_25 and serves the Enhanced FRS only by name;
    • a release-blocking Microcosm gate compares UK candidates with a pinned Enhanced FRS;
    • Microcosm runs its own UK imputations with microcosm-fit in uk_runtime, where new UK imputation work goes; the text names each stage's source survey.
  • microimpute's archive. A new bullet beside the policyengine-us-data one says PolicyEngine archived microimpute on 2026-11-08, tells agents to open no PRs or issues there, and notes that PyPI keeps all of its releases, including the 1.8.1 that uk-data pins.
  • Line 29. Takes microdf off the list of packages Microcosm replaced, since policyengine-core depends on it. Adds one sentence: build new imputation on microcosm-fit and new calibration on microcosm-calibrate, never on microimpute, microcalibrate or L0.
  • Package table. microcosm-fit now succeeds microimpute as well as ad hoc scripts, microcosm-calibrate succeeds microcalibrate and L0 (l0-python), and microcosm-frame succeeds microdf only inside Microcosm.
  • "Institutional knowledge". The old text said the old stack's algorithms "are exactly what Microcosm's fit and calibrate operators do". The new text says Microcosm implements the same families of methods with its own code and defaults, contrasts the two QRFs' defaults for leaf samples and weights, and tells agents not to let a previous-stack fit stand in for a Microcosm one. The heading now reads "previous-stack concepts", since PolicyEngine has not archived microcalibrate or L0.
  • QRF paragraph. "Quality is scored with quantile loss" presented microimpute's metric as current practice. The paragraph now credits that metric to microimpute and names microcosm-fit's held-out scorer, which covers categorical models only. Its last sentence drops the claim that the fit reads the frame's weights "by construction", since a caller can opt out with weights="none".
  • Description. Adds microimpute and microcalibrate to the triggers, so an agent about to reach for either one loads this skill. To stay under the 1,024-character Codex limit (now 1,019), "conditional models" became "models", "build/release gates" became "release gates", and "calibration diagnostics dashboard" became "calibration dashboard".

Evidence from code

Claim Where
PolicyEngine archived microimpute on 2026-11-08 gh api repos/PolicyEngine/microimpute: "archived": true, updated_at 2026-11-08T06:31:54Z; PolicyEngine/microimpute#224 merged 2026-11-08T06:27:46Z
PyPI keeps all of microimpute's releases, including 1.8.1 https://pypi.org/pypi/microimpute/json lists 61 releases and none yanked; uk-data's uv.lock:889-891 pins 1.8.1 from https://pypi.org/simple
Microcosm runs its UK imputations with microcosm-fit Microcosm main 3db65154b, packages/microcosm-build/src/microcosm/build/uk_runtime/: was_wealth.py:900,935, lcfs_consumption.py:1935,1990, etb_vat.py:371,373 and etb_services.py:481,489 call RegimeGatedQRF; spi_income.py:3566 and salary_sacrifice.py:880 load microcosm.fit.QRF (an alias of RegimeGatedQRF, microcosm/fit/__init__.py:118)
The WAS stage imputes student-loan balances was_wealth.py:205-222 (student_loan_balance among the outputs); student_loans.py:93
Microcosm's salary-sacrifice stage trains on the FRS respondents with pay whom the survey asked about salary sacrifice uk/source_stages.json stage note 47 ("The QRF trains only on the 2024-25 FRS asked subset with pay"); uk_runtime/salary_sacrifice.py:95-98 (SALSAC_TRAINING_POPULATION: FRS channel, salary_sacrifice_asked == 1 and employment_income > 0)
Microcosm's WAS, LCFS and ETB stages port uk-data's models uk/source_stages.json stage notes 22, 30, 31, 32 ("Ports incumbent WAS round-8 wealth imputation", "Ports the incumbent LCFS consumption QRF", "Ports ETB VAT imputation", "Ports ETB public-services QRF")
A release-blocking gate compares UK candidates with a pinned Enhanced FRS uk/gates.json uk_input_mass_parity (terminal, release_blocking, enhanced_frs_2024_25.h5 at revision a9e52499); uk_runtime/terminal_gates.py:81
policyengine.py defaults UK simulations to Microcosm and serves the Enhanced FRS only by name policyengine.py main 57b587036 (6.16.0), src/policyengine/data/bundle/manifest.json:106 (default_dataset: microcosm_uk_2024_25), :261-271 (dataset_overlays.uk.enhanced_frs_2024_25); src/policyengine/provenance/manifest.py:756-776 (an overlay "can never alter default resolution")
uk-data's build imputes with microimpute 1.8.1 policyengine-uk-data main ed19e336: policyengine_uk_data/utils/qrf.py:9 (from microimpute.models import QRF), used by the income, wealth, vat, salary_sacrifice, consumption, frs_only and services/etb imputations; uv.lock:889-890 (microimpute 1.8.1)
microdf stays PolicyEngine/policyengine-core master 7c84d941b, pyproject.toml:26 (microdf_python>=1.0.0); no Microcosm pyproject.toml names microdf, and Microcosm's uv.lock pulls it in only through policyengine-core, policyengine-uk and policyengine-us
L0 ships as l0-python PolicyEngine/L0 pyproject.toml:6; uk-data pyproject.toml:42
microimpute's QRF keeps one sample per leaf by default and fits unweighted unless the caller passes weight_col microimpute 1.8.1 (9051a53): microimpute/models/imputer.py:222 (weight_col defaults to None; when set, fit resamples the training rows by it, :222-300) and qrf.py:167 (no max_samples_leaf); main e4cbe025d: imputer.py:247 takes the same weight_col and hands it to the forest as sample_weight (qrf.py:178-212); quantile-forest 1.4.1, which uk-data locks (uv.lock:1783-1784), and 1.4.2 both default max_samples_leaf to 1
microcosm-fit keeps every leaf sample, up to 4,096 per leaf when a forest's target takes 2 to 32 distinct values, and weights every fit unless the caller passes weights="none" Microcosm main 3db65154b, packages/microcosm-fit/src/microcosm/fit/qrf.py: the default max_samples_leaf=None (:1740) keeps all leaf samples (:1712-1719); _fit_forest raises it to _DISCRETE_Y_LEAF_BOUND = 4_096 (:925) when the forest's pre-bootstrap target has 2 to _DISCRETE_Y_UNIQUE_MAX = 32 (:917) distinct values and the caller left max_samples_leaf unset (:1006-1030), and packages/microcosm-fit/tests/engine_free/shared/test_qrf.py:548-568 asserts the cap; the fit reads a Frame's typed weights by default, treats "none" as its only unweighted path, and raises on a DataFrame without explicit weights (qrf.py:1767-1772)
microimpute scores numeric imputations with quantile loss microimpute microimpute/comparisons/metrics.py:33-56 (quantile_loss for numeric variables, log_loss for categorical ones)
microcosm-fit's one held-out scorer covers categorical models microcosm/fit/held_out.py (module docstring; the package defines no scoring function besides categorical_scores)

Not in this PR

  • This PR leaves dashboard/src/data/manifest.json alone. A rebuild would change the data skill's inferred target_repos (adding policyengine-core, dropping policyengine.py) and reshuffle about 1,600 lines of overlap scores. Require PyYAML for the dashboard manifest, check it on Linux in CI, and keep overlaps in artifact order #107 and the repo-coverage branch regenerate the manifest wholesale, and the dashboard's prebuild hook rebuilds it anyway.
  • The analyze-policy pipeline routes UK INVESTIGATE issues to policyengine-uk-data (targets/claude/agents/report-logger.md:61,200,203,273-277, tests/pipeline/test_country_routing.py, .github/workflows/analyze-policy.yml), although policyengine.py defaults UK simulations to Microcosm. skills/policyengine/SKILL.md:250-253 says the pre-2026 datasets "are gone", though policyengine.py still serves enhanced_frs_* by name. Route UK data issues to the repo that builds the run's dataset #110 fixes both.

uv run pytest --ignore=tests/test_skill_examples.py: 208 passed. build_claude_wrapper.py copies the file unchanged.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits November 7, 2026 11:19
The policyengine-data skill told agents that uk-data's Enhanced FRS feeds
the UK Microcosm build and that UK imputations land in policyengine-uk-data,
listed microdf among the packages Microcosm replaced, said microcosm-fit
succeeds only ad hoc scripts, and called the old stack's algorithms exactly
what Microcosm's operators do.

Microcosm UK imputes WAS wealth, LCFS consumption, ETB VAT and services,
SPI incomes and salary sacrifice itself with microcosm-fit (uk_runtime), and
policyengine.py's bundle serves the Enhanced FRS only as a named overlay.
uk-data's build still imputes with microimpute 1.8.1. microdf stays, since
policyengine-core depends on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- State the UK default as what policyengine.py does (defaults UK
  simulations to microcosm_uk_2024_25, serves enhanced_frs_2024_25 only
  by name) instead of a be-verb identity.
- Mark the QRF contrast as an example and say microcosm-fit weights
  every fit unless the caller passes weights="none"; "has no unweighted
  default" and "by construction" left out the opt-out.
- Drop "lower is better" and the italics on "packages".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 3 commits November 8, 2026 14:17
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Microcosm's QRF caps a forest at 4,096 samples per leaf when its target
takes 2 to 32 distinct values (qrf.py _fit_forest, test_qrf.py), so the
skill now says so, and it sets microimpute's weight_col opt-in beside
microcosm-fit's weights="none" opt-out. The section heading no longer
implies that microcalibrate and L0 are archived. The UK stage list splits
in two, introduces WAS, LCFS and ETB, and names the salary-sacrifice
source, and three restated facts go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The salary-sacrifice stage gets its own sentence: it trains on the FRS
respondents with pay whom the survey asked about salary sacrifice. The
institutional paragraph says "previous-stack fit", matching the
section's other names for the old packages, and the QRF paragraph
re-wraps under 100 characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review November 8, 2026 20:27
@MaxGhenis
MaxGhenis merged commit a8d02cf into main Nov 8, 2026
2 checks passed
@MaxGhenis
MaxGhenis deleted the data-skill-old-stack branch November 8, 2026 20:28
MaxGhenis added a commit that referenced this pull request Nov 9, 2026
#108 edited skills/policyengine-data/SKILL.md without rebuilding the
manifest, so main's copy lacked that edit. Rebuilding adds the edited
artifact, one new overlap pair and the 113 scores the edit moves, and
updates repo_coverage. Built from the same corpus, main's builder and
this branch's give the same 326 pairs and the same scores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Nov 10, 2026
Regenerated with `uv run python scripts/build_manifest.py`. Besides the new
skill, its bundle entries and edges, and the bundles' 4.2.0 version, the
rebuild also picks up what earlier merges left unbuilt: the policyengine-data
skill's description and target repositories from #108.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 11, 2026
#108 edited skills/policyengine-data/SKILL.md without rebuilding the
manifest, so main's copy lacked that edit. Rebuilding adds the edited
artifact, one new overlap pair and the 113 scores the edit moves, and
updates repo_coverage. Built from the same corpus, main's builder and
this branch's give the same 326 pairs and the same scores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 11, 2026
…nd keep overlaps in artifact order (#107)

* Stop the manifest build without PyYAML and check the manifest in CI

- build_manifest.py exits 1, writing nothing, when it can't import PyYAML,
  instead of falling back to a subset parser that turned numbers into
  strings and dropped nested fields (which broke the Analyses page). It
  also stops, naming the file, on frontmatter that isn't a YAML mapping.
  The subset parser and the regex category fallback are gone.
- The dashboard's manifest hooks run the builder through uv. manifest:safe
  keeps the committed manifest when the build can't run.
- --check builds the manifest without writing it and exits 1 when the copy
  on disk differs anywhere but generated_at. A new CI job runs it on Linux
  under Python 3.11 and 3.14.
- IDF comes from decimal's correctly rounded ln rather than math.log, which
  rounds as each platform's C library does; shared_terms break ties by term.
- Overlap pairs stay in artifact order, so a one-skill edit rewrites only
  the scores it moves (#106's edit: 242 changed lines instead of 1,084).
  The drawer and the Duplicates view sort pairs by score themselves.
- Tests: a builder without PyYAML, frontmatter types and errors, --check,
  overlap order, guards against the built-in sum() and math.log, and a
  Hypothesis property that overlaps ignore artifact and word order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check the manifest under Python 3.15 in CI, and report a non-object manifest

Python 3.15 has shipped (CPython is at 3.15.2), so the manifest job's
newer entry moves from 3.14 to 3.15. The job's comment called its pair
the oldest and newest Python the repo supports, which 3.14 no longer is.
The test job already runs 3.15.2. On macOS, the committed manifest
matches a fresh build under every Python from 3.11 to 3.15.

--check printed a traceback when the file on disk held JSON that wasn't
an object. It now says so and exits 1, and a test covers that case and
invalid JSON.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rebuild the manifest on top of #108

#108 edited skills/policyengine-data/SKILL.md without rebuilding the
manifest, so main's copy lacked that edit. Rebuilding adds the edited
artifact, one new overlap pair and the 113 scores the edit moves, and
updates repo_coverage. Built from the same corpus, main's builder and
this branch's give the same 326 pairs and the same scores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read the builder's inputs as UTF-8, and pin main()'s exit on bad frontmatter

Review r3 found that every read used the locale's encoding. Under a
Latin-9 locale on Python 3.11, the build stopped on frontmatter that is
fine ("unacceptable character #x0080"); on Windows before 3.15 it would
misread the same text. Every read now goes through read_utf8, and the
manifest is written as UTF-8 with "\n" line endings.

- test_builder_never_uses_the_locale_encoding runs the build and --check
  with EncodingWarning as an error, so any read or write that leaves the
  encoding out fails it on every Python version.
- test_bad_frontmatter_makes_the_script_exit_1_and_write_nothing pins the
  exit status that CI's --check and the predev hook rely on.
- The CI manifest job installs dependencies in its own step, and its error
  no longer calls every failure a stale manifest.
- The IDF comment counts (n, df) pairs, not ratios, and says how the
  decimal result becomes a float.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rebuild the manifest on top of #92

#92 edited the SKILL.md of three skills (policyengine, policyengine-tools,
policyengine-uk), a reference file of a fourth, the reform-comparator agent
and all eight bundles without rebuilding the manifest, so main's committed
manifest no longer matched its sources. On main's corpus, main's builder
and this branch's give the same 324 pairs and scores; only overlap order
and 7 shared_terms lists differ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant