Skip to content

feat: add no_map mode so subjects and objects use their own CURIEs verbatim - #219

Merged
SkyeAv merged 5 commits into
mainfrom
add-no-map-mode
Oct 15, 2026
Merged

SkyeAv merged 5 commits into
mainfrom
add-no-map-mode

Conversation

@SkyeAv

@SkyeAv SkyeAv commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Adds a no_map: true opt-in on NodeEncoding that skips fullmap entity resolution for
statement subjects and objects, so users whose data already carries its own CURIEs pass
those values through as node ids as-is. Enum-ranged qualifiers already bypass the fullmap
through the resolved property; this rolls the same pass-through up to the node level as
an explicit config choice.

Config surface

  • no_map: bool = False on NodeEncoding (src/tablassert/models.py): default keeps
    every existing config resolving exactly as before.
  • Dead-config rejection: no_map with any resolution-only knob (prioritize, avoid,
    exclude_prefixes, exclude_regex, explicit non-default taxon) raises
    no-map-dead-config at config time, matching the nullable-on-literal posture.
  • Literal CURIE check: a method: value literal must be CURIE-shaped after
    prefix/suffix (no-map-bad-curie), so a bad literal fails at config time instead of
    silently emptying the build. method: column cells are audited per row at build time.
  • Qualifiers: Qualifier.resolved stays the single routing predicate
    (vocabulary is None and not no_map); no_map on an enum-ranged qualifier is rejected
    as redundant.

Pipeline

  • lib._node_ops splits node columns into resolved (resolve_batch specs), verbatim
    (no_map), and literal (enum-ranged) groups. An all-verbatim section sends zero specs, so
    the fullmap redb is never opened; mixed statements resolve the mapped side as before.
  • New lib.curie_audit op (registered in PHASE_OF and the runcache cost table):
    drops blank/null-like rows (a missing CURIE cannot form an edge), raises
    no-map-curie-violation on non-blank non-CURIE cells (mirroring
    qualifier_vocabulary_audit), and synthesizes <col>_category = biolink:NamedThing so
    edge_category degrades to biolink:Association like the documented no-role fallback.
  • Graph compile fix: no_map columns emit the <col>_pre_resolution snapshot, which
    _collect_subframes uses to discover node columns; without it verbatim nodes never
    split out of the edges and the RIG audit rejects the empty category lists.

Design

  • Blank vs garbage: blanks drop (missing node id, same as an unresolved term); non-blank
    non-CURIE cells fail the build. Free text must not ship as a KGX identifier.
  • Accepted caveat: no_map nodes carry biolink:NamedThing, so their edges derive
    biolink:Association unless category_override pins a class. No name/taxon enrichment
    exists without a lookup.
  • Deferred: per-prefix bioregistry validation of verbatim CURIEs; a global CLI flag to
    disable mapping for a whole build.

Docs

  • docs/configuration/table.md: no_map row in the NodeEncoding table (keeps the docs
    source-of-truth binding green) plus a No-map mode subsection with a YAML example.

Testing

  • make check -> exit 0: ruff clean, pyright 0 errors, pytest 1235 passed + 1 skipped,
    cargo test ok, cargo clippy -D warnings ok.
  • uv run pytest tests/test_e2e_smoke.py -q -n 0 --no-cov -> 10 passed, including a
    build_pipeline run whose CUSTOM: CURIEs are absent from the redb (proving verbatim
    pass-through) and a negative smoke asserting no-map-curie-violation on free text.
  • Manual build_pipeline against a nonexistent redb path: build OK, nodes emitted with
    verbatim ids and biolink:NamedThing categories, blank row dropped.

Questions for the reviewer

  • Category fallback. biolink:NamedThing keeps nodes valid KGX but loses role
    precision in the RIG summaries; is the generic category the right default, or should
    no_map accept an optional explicit category?

…lidation

Add no_map: bool = False to NodeEncoding so statement subjects and objects
whose data already carries CURIEs can skip fullmap entity resolution and use
the encoded value verbatim as the node id.

- reject no_map combined with resolution-only knobs (prioritize, avoid,
  exclude_prefixes, exclude_regex, explicit non-default taxon) as dead config
- validate no_map literals are CURIE-shaped (after prefix/suffix), so a bad
  literal fails at config time instead of silently emptying the build
- Qualifier.resolved becomes the single routing predicate: enum-ranged
  qualifiers stay verbatim, CURIE-ranged ones resolve unless no_map opts out;
  no_map on an enum-ranged qualifier is rejected as redundant
…ction pipeline

_node_ops now splits node columns into resolved (resolve_batch specs),
verbatim (no_map subject/object and no_map CURIE-ranged qualifiers), and
literal (enum-ranged qualifiers) groups.

- new lib.curie_audit op: drops blank/null-like rows (a missing CURIE
  cannot form a node), raises no-map-curie-violation on non-CURIE cells,
  and synthesizes <col>_category = biolink:NamedThing so edge_category
  degrades to biolink:Association like the documented no-role fallback
- resolve_batch receives zero specs for an all-verbatim section, so the
  fullmap redb is never opened; mixed mapped/verbatim statements resolve
  the mapped side as before
- fullmap_audit naturally skips verbatim columns (it iterates the
  resolved node_columns list)
- register curie_audit in PHASE_OF (encode) and the runcache op-cost
  table
…IE nodes

Add offline build_pipeline smokes for the no_map path:

- verbatim CUSTOM: CURIEs absent from the redb appear in nodes and edges,
  proving no lookup happened; blank-subject row dropped, never emitted
- emitted nodes carry the synthesized biolink:NamedThing category and edges
  reference the emitted node ids
- negative smoke: a non-CURIE cell fails the build with no-map-curie-violation

Also emit the <col>_pre_resolution snapshot for no_map columns: graph
compile discovers node columns through that snapshot, so without it a
verbatim subject/object never split out of the edges into nodes and the
RIG audit rejected the empty category lists.
Add the no_map row to the NodeEncoding field table (keeps the docs
source-of-truth binding green) and a No-map mode subsection covering the
verbatim pass-through, blank-drop/non-CURIE-fail behavior, prefix/suffix
CURIE formation, and the rejected resolution-filter combinations.
…tinct values only

The field description and docs table claimed non-CURIE cells are dropped;
the shipped curie_audit raises no-map-curie-violation on them (mirroring
qualifier_vocabulary_audit), so both now state: blanks drop, non-CURIE
cells fail the build. Also judge CURIE shape on distinct non-blank values
so the per-element check does not scale with row count.
@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8d7e3df3-e431-466b-98ea-d78ebadd70cf
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@SkyeAv SkyeAv changed the title add no map mode feat: add no_map mode so subjects and objects use their own CURIEs verbatim Oct 15, 2026
@SkyeAv
SkyeAv merged commit 32f4478 into main Oct 15, 2026
5 checks passed
SkyeAv added a commit that referenced this pull request Oct 15, 2026
Cut 21.0.0 and bump the package version in pyproject.toml, CITATION.cff,
and uv.lock.

Major: two breaking changes ship since 20.0.0. Derived-path builds emit
section source record URLs on the upstream repository supporting sources
entry (infores:pubmed-central for PMC sections) instead of the primary
graph-infores entry, and the inlined supporting Study is named after the
table config file plus its 1-based section index (#220); validate-infores
is folded into validate-kgx as the knowledge-source identity half, and
the standalone validate-infores command is gone (#218). Also adds no_map
on NodeEncoding to pass subject/object CURIEs through verbatim (#219) and
normalizes documentation prose to plain ASCII (#217).

Changelog:
- Versioned the Unreleased section as 21.0.0 - 2026-10-15.
- Added the missing #218 (breaking, with migration note), #219, and #217
  entries so no merged PR ships unmentioned.

Docs: none needed here; the release commit only versions existing prose.

Testing:
- make check -> ruff clean, pyright 0 errors, pytest 1239 passed + 1
  skipped, cargo test ok, cargo clippy --all-targets -- -D warnings clean
- uv run mkdocs build --strict -> pass
SkyeAv added a commit that referenced this pull request Oct 16, 2026
…olve (#221)

* feat!: remove the no_map node mode so subjects and objects always resolve

`no_map: true` on a statement subject or object (#219, released in 21.0.0)
skipped fullmap entity resolution and emitted the cell value as the node id
verbatim. It breaks the output: with no lookup there is no role to derive, so
the build pinned every verbatim node's category to `biolink:NamedThing` and
each edge derived from it degraded to `biolink:Association`, losing the typed
predicate categories KGX consumers expect. The mode is removed rather than
patched, restoring the 20.0.0 resolution path.

- `NodeEncoding.no_map` is gone, along with the `no-map-dead-config` and
  `no-map-bad-curie` config validators, the `no-map-curie-violation` code, and
  the `CURIE_PATTERN`/`is_curie` helpers that only served them.
- `lib.curie_audit` and its `Tcode._node_ops` verbatim branch are gone, so
  subject and object columns go back through the shared `resolve_batch`, plus
  the matching `PHASE_OF` and `runcache.OP_COST` entries.
- CURIE-ranged qualifiers always resolve again: `Qualifier.resolved` is
  `vocabulary is None`, so enum-ranged qualifiers still pass their vocabulary
  tokens through verbatim and nothing else does.
- The `no_map` row and the "No-map mode" section are removed from
  `docs/configuration/table.md`, and the tests written for the mode are gone.

Migration: drop `no_map: true` from subject and object encodings. Terms resolve
as they did before 21.0.0; tables that already carry CURIEs also resolve, since
the fullmap indexes CURIEs and their equivalent identifiers as terms.

* feat: explain the removed no_map key to configs that still set it

With `no_map` deleted from `NodeEncoding`, a table config written against
21.0.0 failed with pydantic's bare "Extra inputs are not permitted", which
names no history and offers no migration step for an unattended pipeline run.
`models.DEPRECATED_KEYS` is the repo's scaffold for exactly this: the
before-hook warns, and `extra="forbid"` still rejects the key, so the config
never builds the output the removal exists to prevent.

- `no_map` is registered with a note that says the mode is gone, why (verbatim
  nodes pinned to `biolink:NamedThing` flattened edge typing), and what old
  configs should expect now: a CURIE cell resolves back to its own CURIE because
  the fullmap indexes CURIEs as terms, with the usual `taxon`,
  `prioritize`/`avoid`, and `exclude_*` rules still applying.
- The key is rejected on presence, not truthiness, so `tests/test_models.py`
  pins the warn-then-reject path for both `no_map: false` and `no_map: true`.
- The two existing deprecation tests' "registry is EMPTY" wording is corrected,
  since the registry now names one removed key.
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