Skip to content

feat: assert every node has an id and every edge has subject, predicate, and object in final graph QC - #107

Merged
SkyeAv merged 1 commit into
mainfrom
qc-required-slots
Aug 21, 2026
Merged

feat: assert every node has an id and every edge has subject, predicate, and object in final graph QC#107
SkyeAv merged 1 commit into
mainfrom
qc-required-slots

Conversation

@SkyeAv

@SkyeAv SkyeAv commented Aug 21, 2026

Copy link
Copy Markdown
Owner

The --qc stage-7 study pass now asserts the core KGX join slots outright: every node must carry a non-empty id, and every edge must carry non-empty subject, predicate, and object — completing the required-slot ladder begun by #106's unnamed-nodes.

Study assertions (src/tablassert/study.py)

  • unidentified-nodes: a node record whose id key is missing, null, or strips to empty fails the study. Since the id is exactly what's absent, examples key on the node's name (or <no name>), making the offender identifiable from the stderr summary.
  • incomplete-edges: an edge record missing any of the three core slots — missing key, null, or strips-to-empty — fails, counted per slot (e.g. predicate (2)), matching the whitespace-values example format.
  • Node name already asserted by feat: assert no unnamed nodes and no null or empty values in final graph QC #106's unnamed-nodes; that check is untouched. A record with neither id nor name deliberately fires both assertions (pinned by test).

Design

  • Conventions mirror the name assertion. Non-string, non-null values pass (no writer emits them); the assertions target absent slots, not JSON types. strip_nulls (rust/src/json.rs) deletes a null slot outright rather than emitting it, so on pipeline output a hit means the slot was null upstream and the record shipped broken — exactly the condition these assertions exist to catch loudly.
  • Full e2e suite green — no legitimate build path emits slotless records, so the assertions tighten the contract without breaking real builds.

Docs

  • docs/cli.md (--qc row), the build_kg docstring in src/tablassert/cli.py (rendered by --help), and CHANGELOG.md (Unreleased).

Testing

  • uv run --no-sync pytest -q --no-cov1056 passed, 15 skipped
  • uv run --no-sync pytest tests/test_study.py --no-cov -q28 passed
  • uv run --no-sync ruff check + ruff format --check → clean; uv run --no-sync pyright on changed files → 0 errors
  • One CODE_REVIEWER pass: verdict approve, all three findings (non-string-slot test gap, variable shadowing, fixture cross-fire) addressed

@coderabbitai

coderabbitai Bot commented Aug 21, 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: Pro Plus

Run ID: fcec136e-753e-4076-b724-c43c8ec6ea9a


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 merged commit 104927d into main Aug 21, 2026
5 checks passed
@SkyeAv
SkyeAv deleted the qc-required-slots branch August 21, 2026 17:48
SkyeAv added a commit that referenced this pull request Aug 24, 2026
Cut 13.0.0 and bump the package version in pyproject.toml, uv.lock, and
CITATION.cff.

Major: three breaking changes since 12.1.0. The inlined supporting study now
carries current Biolink Study metadata with disjoint ids and names and no `#`
composition; biolink-model 4.4.4 types the statistical edge slots, so
`effect_size` ships as a real JSON number and `statistical_significance_qualifier`
rides the edge as a bare enum token (both 99099a1); and an unpaired
`effect_size`/`effect_type` half is now DROPPED with an
`UnpairedEffectAnnotationWarning` instead of failing the section, retiring the
`annotation-effect-size-without-type` / `annotation-effect-type-without-size`
codes (#105). Also ships the two final-graph QC assertion sets (#106, #107), the
resolve_batch single-materialization win (#110), and the agent's task
pre-rendering, planning-off, improve-round cap, and build memoization (#113).

The legacy TableConfigs importer (#105) is deliberately absent from the
changelog: it landed and was removed (58787f4) inside this window, so it never
appeared in a released version and is a net no-op for users.

main was red at 7140c37; fixed here so the release is cuttable. Both failures
are #112 fixtures/expectations written against biolink-model 4.4.3 and merged
after the 4.4.4 bump landed:

- The vendored DAKP configs listed `AffinityMeasurement` in `avoid:`, a class
  4.4.4 renamed to `ProteinLigandAssayResult`. DAKP generates `avoid` as the
  sorted complement of each side's prioritize tuple, so the old name is a 4.4.3
  generation artifact rather than an intentional deviation; rewritten in place
  and recorded in the fixture README.
- test_copysign_transformation_in_pipeline asserted `effect_size == "-0.85"`,
  the pre-4.4.4 `{:.4g}` string form. It is a real JSON number now.

Deliberate Biolink departures are untouched: p-value columns keep their
controlled scientific notation despite the model typing them `float`, and
`approval_ids` remains a curated pending pass-through.

docs/cli.md's validate-kgx section still printed `biolink-model 4.4.3` and
claimed `effect_size`/`effect_type` were pending; rewritten around the fields
that are actually pending today (`approval_ids` plus the KGX denormalized
carryovers), noting the pair graduated when 4.4.4 shipped #1774.

Testing:
- uv run pytest -q -> 1047 passed, 15 skipped (94% coverage)
- uv run ruff check . && uv run ruff format --check . && uv run pyright -> clean / 0 errors

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZQ8rLfhvtyErcq9S4Ao6b
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