Skip to content

Add Axiom graph adapter surfaces and simulate.rules_by_ref@1 (NZ v0, G1) - #1118

Merged
MaxGhenis merged 2 commits into
mainfrom
nzhub/g1-axiom-graph-adapter
Oct 7, 2026
Merged

MaxGhenis merged 2 commits into
mainfrom
nzhub/g1-axiom-graph-adapter

Conversation

@MaxGhenis

Copy link
Copy Markdown
Contributor

Salvage the live parts of #821's adapter onto main's axiom.py and add what
graph nodes need to run the real Axiom engine (NZ graph plan, package G1):

  • NZ_SCHEMA (persons in households and families) and NZ_NESTING. With
    AxiomEngine(nesting=...), a family split across two households is refused
    from the person memberships alone, whatever the weights. Add NZ transport scaffold and real-Axiom Frame bridge #821's explicit
    {group}_{parent}_id relation-column check is ported; a contract-required
    relation column is enforced at export only.
  • AxiomPeriod(start, end, kind) and periods={label: AxiomPeriod}: "2026-27"
    resolves to explicit tax_year bounds, and an unmapped label fails closed.
  • output_dtypes="graph": judgments -> int64 codes, integers -> int64,
    decimals -> float64, bool -> bool, all lossless; text and date outputs are
    refused before the engine runs. graph_dtype() names the Owned dtype token.
    The default ("native") is unchanged, so Belgian behaviour is unchanged.
  • axiom_engine_ref(): canonical JSON of the engine commit, wheel SHA-256,
    RuleSpec commit, module path and SHA-256, the whole-root digest
    (rulespec_tree_digest, the graph's directory-identity formula), arithmetic,
    output dtypes, schema, entity names, period map and nesting. The reference
    pins the adapter: it refuses an adapter that already compiled, and every
    later compile or reference refuses a root whose digest moved. A git
    checkout root must hold exactly the declared commit (no modified,
    untracked or ignored files); a root with symlinks is refused.
  • assert_no_relations(engine, entity) names the module path when a dense
    program declares relations.
  • New microcosm.frame.rules_kernels.SimulateRulesByRefKernel
    (simulate.rules_by_ref@1): {engine_ref: RulesEngine}, one
    SimulateRulesKernel per binding by composition, so each node keeps
    simulate.rules@1's params contract, outputs check, all-rows rule and
    receipt. Its implementation hash binds rules_kernels.py, kernels.py, the
    frame bundle/rules/schema modules and each distinct adapter class, not the
    set of refs. kernels.py is untouched (parity pin).

Invariants (Hypothesis property tests):

  • A family nests in exactly one household (refused iff some family spans
    households).
  • Only households carry explicit weights in an NZ export (any explicit
    person or family weight blocks the write under the NZ contract; families
    resolve their household's weight).
  • An unmapped period label fails closed (resolves iff mapped).
  • Graph-typed judgments are lossless (codes -1/0/1 round-trip bit-for-bit).
  • engine_ref moves iff a pinned byte moves (relocation-invariant; any
    one-byte edit moves it).
    Differential tests: rulespec_tree_digest vs microcosm.graph.keys
    (_directory_identity and source_content_key); simulate.rules_by_ref@1 vs
    simulate.rules@1 on kernel contexts and in two-engine run_graph runs.

Not ported (salvage note): #821's Decimal-object input path
(_checked_decimal_array) and its Decimal HDF write path (fixed-format
tables for Decimal columns). Graph columns cannot hold object dtype, so they
stay on branch codex/nz-transport-microcosm (9f7dead) for a later lane.

Coordination: drafts #826 (feature/axiom-relation-bindings, 952952a) and
#822 (feat/axiom-concept-coverage-be, e6d3c92) also edit axiom.py. Both
already conflict with main there (2 hunks each). With this change, merge-tree
shows 5 and 3 hunks, all additive (imports, all, docstring, the period
line in materialize, appended helpers); no new conflicted files.

Invariants, acceptance and tests (from the build lane report; independent review pending)

Tests run

  • Baseline before changes (3 adapter/kernel files): 21 passed, 33 skipped.
  • Engine-free, 11 covering files (both new files, the existing adapter, concept-mapping and kernel tests, the rules-engine contract, the serializer registry, graph parity pins, acceptance H parity, the interface lock, graph keys): 231 passed, 37 skipped.
  • New files only: 146 passed, 4 skipped (the 4 engine-present tests).
  • Local engine build, plus POPULACE_RULESPEC_BE pointing at a local rulespec-be checkout, 5 frame files: 203 passed, 1 skipped. The skip is the "engine absent" test. All Belgian adapter tests pass, including the pilot slice.
  • G1 engine-present tests alone: 4 passed.
  • ruff check . is clean, ruff format --check is clean on the 4 touched files, and tools/ci_test_plan.py verify passes. A local microcosm-frame wheel build matches the source tree and includes rules_kernels.py.

Every pytest run used -p no:cacheprovider --basetemp=.hub-scratch/pytest -o tmp_path_retention_policy=failed. I deleted .hub-scratch, .hypothesis and .ruff_cache afterwards. The workspace .venv (857 MB, git-ignored) is still there.

The local engine was the checkout's Python package at 2840b57 plus a native library built on 6 Sep for CPython 3.14. It is not the MC1 pin. It needed a non-free-threaded 3.14 scratch venv, because the workspace venv is free-threaded 3.14t and cannot load it.

Shared files and owners

Part of the Microcosm NZ v0 graph plan (Max ruling d962). Built by the NZ hub on an Opus build lane; the hub verified scope (no frozen graph files, uk_runtime/us_runtime or frame/kernels.py touched) before committing.

🤖 Generated with Claude Code

Salvage the live parts of #821's adapter onto main's axiom.py and add what
graph nodes need to run the real Axiom engine (NZ graph plan, package G1):

- NZ_SCHEMA (persons in households and families) and NZ_NESTING. With
  AxiomEngine(nesting=...), a family split across two households is refused
  from the person memberships alone, whatever the weights. #821's explicit
  {group}_{parent}_id relation-column check is ported; a contract-required
  relation column is enforced at export only.
- AxiomPeriod(start, end, kind) and periods={label: AxiomPeriod}: "2026-27"
  resolves to explicit tax_year bounds, and an unmapped label fails closed.
- output_dtypes="graph": judgments -> int64 codes, integers -> int64,
  decimals -> float64, bool -> bool, all lossless; text and date outputs are
  refused before the engine runs. graph_dtype() names the Owned dtype token.
  The default ("native") is unchanged, so Belgian behaviour is unchanged.
- axiom_engine_ref(): canonical JSON of the engine commit, wheel SHA-256,
  RuleSpec commit, module path and SHA-256, the whole-root digest
  (rulespec_tree_digest, the graph's directory-identity formula), arithmetic,
  output dtypes, schema, entity names, period map and nesting. The reference
  pins the adapter: it refuses an adapter that already compiled, and every
  later compile or reference refuses a root whose digest moved. A git
  checkout root must hold exactly the declared commit (no modified,
  untracked or ignored files); a root with symlinks is refused.
- assert_no_relations(engine, entity) names the module path when a dense
  program declares relations.
- New microcosm.frame.rules_kernels.SimulateRulesByRefKernel
  (simulate.rules_by_ref@1): {engine_ref: RulesEngine}, one
  SimulateRulesKernel per binding by composition, so each node keeps
  simulate.rules@1's params contract, outputs check, all-rows rule and
  receipt. Its implementation hash binds rules_kernels.py, kernels.py, the
  frame bundle/rules/schema modules and each distinct adapter class, not the
  set of refs. kernels.py is untouched (parity pin).

Invariants (Hypothesis property tests):
- A family nests in exactly one household (refused iff some family spans
  households).
- Only households carry explicit weights in an NZ export (any explicit
  person or family weight blocks the write under the NZ contract; families
  resolve their household's weight).
- An unmapped period label fails closed (resolves iff mapped).
- Graph-typed judgments are lossless (codes -1/0/1 round-trip bit-for-bit).
- engine_ref moves iff a pinned byte moves (relocation-invariant; any
  one-byte edit moves it).
Differential tests: rulespec_tree_digest vs microcosm.graph.keys
(_directory_identity and source_content_key); simulate.rules_by_ref@1 vs
simulate.rules@1 on kernel contexts and in two-engine run_graph runs.

Not ported (salvage note): #821's Decimal-object input path
(_checked_decimal_array) and its Decimal HDF write path (fixed-format
tables for Decimal columns). Graph columns cannot hold object dtype, so they
stay on branch codex/nz-transport-microcosm (9f7dead) for a later lane.

Coordination: drafts #826 (feature/axiom-relation-bindings, 952952a) and
#822 (feat/axiom-concept-coverage-be, e6d3c92) also edit axiom.py. Both
already conflict with main there (2 hunks each). With this change, merge-tree
shows 5 and 3 hunks, all additive (imports, __all__, docstring, the period
line in materialize, appended helpers); no new conflicted files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apply findings 1, 2, 3 and 5 of the independent review of 105c062:

- _require_clean_checkout now refuses any tracked file that
  `git ls-files -v` tags S (skip-worktree) or lowercase
  (assume-unchanged). git status reports no edit to such a file, so an
  edited, flagged file used to pass as a clean checkout at the declared
  commit. The check runs before the HEAD comparison and refuses a flagged
  file even when it is unedited.
- AxiomEngine._require_pinned_tree refuses a symbolic link under the root
  before re-hashing it. The tree digest does not descend into a linked
  directory, so a directory link added after axiom_engine_ref left the
  pinned digest unchanged, and later compiles went ahead.
- The axiom_engine_ref docstring now says the engine commit and wheel
  SHA-256 are pins the caller declares. They are recorded as given and not
  read from the installed engine, and the directory digest is the
  authority. Binding the installed engine is left for a later lane.
- A new test checks that SimulateRulesByRefKernel's implementation hash is
  the same for all six binding orders of adapter classes from three
  modules. The hash must equal source_hash over the classes in
  (module, qualname) order. The test kills the review's surviving
  -id(cls) sort-key mutant and an unsorted insertion-order mutant; every
  earlier test let both through.

New tests: a hidden-edit refusal for each flag, an acceptance once each
flag is cleared, and a refusal of a symlink added after the reference.
All five fail on 105c062 with "DID NOT RAISE". Findings 4, 6 and 7 are
outside this change: 4 lands with G2's export contract, 6 is a separate
docstring nit (native text and date outputs are arrays, not lists), and
7 is the missing CI engine job.

This narrows the PR description's checkout claim to: "A git checkout
root must be clean and at the declared commit: no modified, untracked or
ignored files, and no tracked file flagged skip-worktree or
assume-unchanged (git status hides edits to those). A root holding a
symbolic link is refused when the reference is made and on every later
compile." The engine pins are described as "the declared engine commit
and wheel SHA-256 (recorded as given, not read from the installed
engine)".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit 4121f27 into main Oct 7, 2026
10 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Merged by the NZ hub (session local_e407a527) at 065d8eb with --merge. Gates: gh pr checks exit 0; MERGEABLE; not draft; no CHANGES_REQUESTED review. Reviewed head: 065d8eb. Independent Opus reviews: APPROVE_WITH_NITS at 105c062 (jobs/review-microcosm-1118/out-033221.md), then delta APPROVE_WITH_NITS at 065d8eb (out-053144.md), which confirms findings 1, 2, 3 and 5 closed. Needs Max: no. Open non-blocking nits (submodule clean-checkout gap; docstring 'Python string lists'; no engine CI job) are tracked as follow-ups. Part of Microcosm NZ v0 (d962).

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