Skip to content

feat: add duplicate-key QC gate to qcModelChecks#1056

Merged
edkerk merged 16 commits into
developfrom
ci/qc-duplicate-key-gate
Jul 12, 2026
Merged

feat: add duplicate-key QC gate to qcModelChecks#1056
edkerk merged 16 commits into
developfrom
ci/qc-duplicate-key-gate

Conversation

@edkerk

@edkerk edkerk commented Jul 12, 2026

Copy link
Copy Markdown
Member

Main improvements in this PR:

Add a duplicate-key QC gate so the class of bug behind the MAR01801 breakage in #1055 is caught with a clear message instead of an opaque cobra crash.

Each metabolite, reaction and gene is written as a YAML ordered map (!!omap). A repeated key within one - two name fields on a reaction, or the same metabolite twice in a reaction's stoichiometry - is written and re-read happily by RAVEN, but makes cobra.io.load_yaml_model raise a bare AssertionError with no location (which is how the recent checks and yaml-validation failures surfaced).

  • Add check_no_duplicate_keys to code/test/qcModelChecks.py: a PyYAML event scan that runs before the cobra load and fails the build with a clear message naming the entry, key and line numbers. Covers both duplicate fields and duplicate stoichiometry metabolites.
  • Install pyyaml in the model-qc checks job.

Validated locally: the clean model passes (the gate returns nothing, growth 124.9 ok); an injected duplicate name field and an injected duplicate stoichiometry metabolite are both reported with entry id and line numbers.

No model changes.

I hereby confirm that I have:

  • Any removed reactions and metabolites have been moved to the corresponding deprecated identifier lists in data/deprecatedIdentifiers/.
  • This PR has develop as target branch, and will be resolved with a squash-merge.
  • This PR has main as target branch, and will be resolved with a merge commit.

A repeated key inside a metabolite/reaction/gene !!omap block (e.g. two name
fields on one reaction, as happened with MAR01801) is tolerated by RAVEN's
YAML reader/writer but makes cobra.io.load_yaml_model raise a bare
AssertionError with no location, failing the QC and MEMOTE jobs opaquely.

Add check_no_duplicate_keys, a PyYAML event scan that runs before the cobra
load and fails the build with a clear message naming the entry, key and line
numbers. Covers both duplicate fields and duplicate stoichiometry
metabolites. Install pyyaml in the model-qc checks job.
@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

Model quality report

⚠️ 6 pre-existing finding(s), no regressions vs develop. Non-blocking.

Structural checks

Duplicate keys (model unloadable) and no growth block the merge; the other rows are non-blocking.

Check Result Δ vs develop
Duplicate !!omap keys 0 new
Reactions with no metabolites 0 new
Model / annotation-table inconsistencies 0 new
Growth (biomass producible) 125 0

Model QC reports

Check Result Δ vs develop
Metabolites missing formula 0 -7
Metabolites missing charge 0 0
Reaction bound / GPR issues 0 0
Exact-duplicate reaction groups 0 new
Unused metabolites 0 new
Unused genes 0 new
Malformed cross-references 59 0 ⚠️
Cross-refs inconsistent across compartments 59 0 ⚠️
MEMOTE score (%) 20.2 0

MACAW and mass/charge balance

Check Result Δ vs develop
Reactions flagged by MACAW dead-end test 2510 0 ⚠️
Reactions flagged as MACAW duplicates 377 0 ⚠️
Mass-imbalanced reactions 87 0 ⚠️
Charge-imbalanced reactions 240 0 ⚠️

Gene essentiality (Hart 2015)

cellLine TP TN FP FN accuracy sensitivity specificity F1 MCC
DLD1 38 2158 60 280 0.8659 0.1195 0.9729 0.1827 0.1588
GBM 34 2137 64 300 0.8564 0.1018 0.9709 0.1574 0.1276
HCT116 47 2181 55 308 0.8599 0.1324 0.9754 0.2057 0.1906
HELA 32 2241 70 250 0.8766 0.1135 0.9697 0.1667 0.1332
RPE1 15 2179 83 258 0.8655 0.05495 0.9633 0.08086 0.02935
all 7 2379 95 112 0.9202 0.05882 0.9616 0.06335 0.02199

❌ = a count rose vs the target branch (regression) · ⚠️ = a pre-existing non-zero finding (non-blocking) · ⏳ = still running. Counts link to the CSV listing the exact entries.

Results for commit ec1e12d.

Full workflow run · this comment is edited on subsequent runs

@edkerk edkerk added this to the 2.0.1 milestone Jul 12, 2026
edkerk and others added 2 commits July 12, 2026 22:12
…and MEMOTE detail (#1056)

- buildReport.py: render the MACAW and mass/charge balance findings (dead-end,
  duplicate, and mass- and charge-imbalanced reaction counts) as a status table
  with the value, the change versus the target branch and an icon, matching the
  Model QC checks table, instead of embedding the prose summary. A result set not
  committed yet shows an hourglass.
- Fetch macaw_results.csv and balance_results.csv from the target branch in both
  QC workflows so the new table can show a delta.
- README test-results list: give each result set its own line, updated by its own
  workflow (model QC checks, MEMOTE, MACAW and mass/charge balance, gene
  essentiality). The template carried two "(QC)" lines that the qc-tests sed both
  rewrote, and had no MEMOTE or MACAW entry; the Model QC checks and memote jobs
  now each update their own line.
- memoteSnapshot.py: add a per-test detailed-score table grouped by section, in
  addition to the section summary.
edkerk and others added 12 commits July 12, 2026 22:41
#1056)

The adversarial review surfaced two gaps: the checks that block a merge
(duplicate/empty/inconsistent reactions, growth) left their findings only in
the workflow log, and the pull-request comment covered only the soft report
checks, so it could read all-green while a required check was red. This
reworks the pipeline so every failing check has a committed detail file and
the comment shows a clear problem-or-not verdict with a delta vs the target
branch.

- Consolidate the structural checks into code/test/qcModelChecks.py: gates
  (duplicate !!omap keys, reactions with no metabolites, model vs
  annotation-table consistency incl. deprecated ids and the spontaneous
  column, growth) and reports (metabolite completeness, reaction sanity,
  exact-duplicate reactions, unused entities), each writing a detailed CSV.
  On a growth failure it writes the blocking biomass precursors.
- buildReport.py: a one-line verdict (merge blocked / regressions / running /
  clean), a Build gates table, a Model QC reports table and the MACAW/balance
  table, each value linked to its CSV with a delta and an icon. Each result
  set is stamped with the head commit (qc_checks/memote/macaw.sha) so a set
  that has not re-run for the current commit shows as pending, not stale.
- The Model QC checks job commits the detail and posts the comment even when
  a gate fails, then fails the build, so the failure is visible in both.
- Remove the now-duplicated memoteTest.py and sanityCheck.py; trim
  yaml-validation to the YAML lint. Guard the MACAW step so a crash no longer
  skips the balance report and comment; balanceTest.py records reactions it
  cannot check instead of silently dropping them.

Branch protection should require the "Model QC checks / checks" job (which now
owns the structural gates) in place of "yaml-validation".
The 7 formula-less metabolites (MAM10001-3 generic class sinks; MAM10012-15
*_pool_biomass) intrinsically have no molecular formula, so flagging them as
"missing formula" is noise. Exclude them via an explicit documented set -
not the MAM10xxx id range, which also holds real metabolites (bile acids,
CoAs). Missing-formula count 7 -> 0.
- Link finding counts to their CSV with an absolute repo blob URL on the PR
  branch, so the links resolve to the file instead of a PR-relative path.
- Icon rule: red cross when a count rose vs the target branch (a regression
  this PR introduced), warning sign when a count is non-zero but did not rise
  (a pre-existing, non-blocking finding), check mark when zero. Growth stays
  pass/fail; the MEMOTE score warns only when it drops.
- A result set is treated as current only when its commit stamp matches the
  PR head; an absent or older stamp now shows the rows as running (hourglass)
  instead of a previous run's numbers.
- Empty reactions and model/annotation inconsistencies are non-blocking
  reports (a rising count still flags as a regression); only duplicate keys
  (model unloadable) and no growth block the merge.
@edkerk
edkerk merged commit 23155a1 into develop Jul 12, 2026
@edkerk
edkerk deleted the ci/qc-duplicate-key-gate branch July 12, 2026 21:43
edkerk added a commit that referenced this pull request Jul 14, 2026
Reconcile the CI migration with the QC consolidation (#1056) and combined QC
workflow (#1059) merged into develop meanwhile:
- gene-essentiality.yml: keep this branch's pull_request guard and updated comment
  wording, but post via github-script (develop dropped the deprecated comment-pr
  action and the commentGeneEssential.md template).
- yaml-validation.yml: accept its removal; model-file-checks.yml now does the YAML
  lint.
- model-file-checks.yml: drop the model/annotation consistency step - sanityCheck.py
  was removed on develop and its check is now in qcModelChecks.py (Model QC workflow).
- README.md: use develop's model QC / MEMOTE / MACAW result mentions.
@edkerk edkerk mentioned this pull request Jul 15, 2026
3 tasks
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