Skip to content

chore: version 2.1.0 - #1123

Merged
edkerk merged 52 commits into
mainfrom
release/2.1.0
Oct 5, 2026
Merged

edkerk merged 52 commits into
mainfrom
release/2.1.0

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Short summary:

This release contains three sets of changes:

2.0.1 2.1.0
Reactions 12877 12748
Metabolites 8460 8374
Genes 2848 2870

Compared with 2.0.1, 26 reactions were added and 155 removed (duplicates, unsupported or chemically incorrect reactions), 211 reactions had their stoichiometry corrected, 92 their gene-reaction rule, and 259 metabolites their formula or charge.

Longer details

Each reaction, metabolite and gene that differs between 2.0.1 and 2.1.0 is listed once, with its final change, under the last pull request that changed it; removed identifiers are kept in data/deprecatedIdentifiers/. Changes to names and annotations (cross-references, EC numbers, references, InChI, the spontaneous flag) are not listed per entity. Metabolite merges are written as base ids ("A → B" merges A into B in every compartment where it occurred).

edkerk and others added 30 commits August 18, 2026 22:52
The gene-essentiality run calls a gene essential when its knockout breaks any of
the 57 essential tasks. That list mixes viability tasks (GR growth, ER energy and
redox) with capability tasks (SU, BS, IC), so genes needed only for a capability,
such as the ETF complex for beta-oxidation, are scored as false positives against
the Hart 2015 proliferation screen (issue #1076).

- taskEssentialGenes: add find_task_essential_categories, reporting which task
  categories each knockout breaks. The scan and its progress logging are factored
  into shared helpers; find_task_essential_genes keeps its early exit and
  behaviour.
- evaluateHart2015Essentiality: add bayes_factors, exposing the unthresholded
  Hart Bayes Factors in model-gene space for graded comparisons.
- gradedEssentiality: new analysis entry point reporting, per cell line, the task
  categories behind each call, the single-gene-deletion biomass growth ratio under
  the growth task medium, both binary scorings (all tasks and viability only) and
  the threshold-free AUROC/AUPRC of the growth ratio against the Bayes Factors.

Analysis only: no model changes and not wired into CI.
…ality

Replaces the all-task binary scoring with the graded, task-scoped analysis, so a
gene essential only for a capability task (SU, BS, IC) is no longer counted as a
proliferation prediction alongside viability tasks (GR, ER). See issue #1076.

- gene-essentiality.yml runs gradedEssentiality.py, which now overwrites the usual
  gene-essential.csv and gene-essential_summary.md; the posted comment explains the
  viability/capability split.
- geneEssentiality.py is removed: the graded run reports the all-task scoring too,
  so nothing is lost.
- The graded scoring (evaluate_graded, summary_to_markdown, auroc, auprc) lives in
  evaluateHart2015Essentiality.py, which needs only pandas, so /reevaluate
  gene-essentiality keeps working from the committed matrix without a solve.
- reevaluateGeneEssentiality.py parses the new matrix, which carries the task
  categories and growth ratio per gene.
- The growth ratio is rounded to a fixed precision before scoring and average
  precision is computed over tied scores as a group, so the summary regenerated
  from the committed matrix is identical to the one the run produced.
- Results regenerated from the graded run; see the pull request for provenance.
A result file produced for the first time is untracked in the run that writes it,
but is tracked at the branch tip as soon as an earlier run has committed it. Plain
git stash leaves untracked files in the working tree, so the checkout that follows
refuses to overwrite them and the job fails after the run has already finished.
…-metrics

test: score gene essentiality per task category (replaces all-task binary scoring)
* chore: remove legacy getINITModel2 gene-essentiality code

Gene essentiality now runs the Python ftINIT (tINIT2) pipeline
(code/test/geneEssentiality.py + estimateEssentialGenes.py +
evaluateHart2015Essentiality.py). Remove the superseded MATLAB implementation:

- tINIT/getINITModel2.m          old INIT algorithm; RAVEN ftINIT is its successor
- tINIT/restoreModelGrRules.m    orphan; restored grRules the old tINIT stripped
- test/estimateEssentialGenes.m  MATLAB; only caller of getINITModel2
- test/evaluateHart2015Essentiality.m  MATLAB

The Python equivalents, the Hart2015 data/results and the (already-Python)
gene-essentiality workflow are the current pipeline and are untouched.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]
* feat: add human_gem Python package with load_model()

First step towards a pip-installable Python interface to Human-GEM, on
raven-toolbox + cobrapy. human_gem.load_model() loads Human-GEM as a fully
annotated cobra model - reading the YAML via raven-toolbox and merging the
cross-references and SBO terms from the annotation tables, which a bare
cobra.io.load_yaml_model does not do:

    import human_gem
    model = human_gem.load_model()   # 12877 rxns, 8460 mets, cross-refs attached

- pyproject.toml (deps: cobra, pandas, raven-toolbox)
- human_gem/io.py     load_model(model_dir=None, annotate=True)
- human_gem/annotation.py  TSV -> MIRIAM/SBO merge (packaged home of the logic
  currently duplicated in code/annotateGEM.py)

Verified locally: load_model() attaches cross-references to 12698/12877
reactions and 8449/8460 metabolites; annotate=False returns the bare model.

Next increments: reduce code/annotateGEM.py to a shim importing from human_gem
(single source of truth) + install the package in CI; then promote the ftINIT
essentiality and task helpers into the package.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* Parallelize gene-essentiality across cell lines via a build matrix (#1079)

* feat: parallelize gene-essentiality across cell lines via a build matrix

The gene-essentiality workflow built and scored all five Hart 2015 cell-line
models in one sequential loop in a single job, taking hours end to end even
though every cell line is independent of the others.

`gradedEssentiality.py`: had no way to build a subset of cell lines, so
splitting the work across parallel processes or jobs was not possible.
Adds `--shard-index`/`--shard-count` to select a slice of cell lines, and
`--aggregate-only` to combine every cell line's checkpoint into the final
report without rebuilding any of them.

`gene-essentiality.yml`: ran Step 1 (prepINITModel) and every cell line's
ftINIT build and essentiality scan in one job, so wall-clock time scaled
with the number of cell lines. Splits into a `build` job matrixed one shard
per cell line and an `aggregate` job that combines their checkpoints into
the committed report; the `diagnose` path is unchanged but now runs as its
own job instead of an early exit inside the shared script.

* chore: update model QC results [skip ci]

* perf: dedupe and parallelize the gene-essentiality knockout scan

The per-tissue scan in taskEssentialGenes.py solved thousands of small, nearly
identical LPs sequentially, so its runtime did not benefit from genes sharing a
knockout or from the extra cores a CI runner has.

- `_scan`: genes that disable the identical reaction set (subunits of the same
  AND-linked complex, e.g. an ETC complex) were each solved separately against
  every task instead of once per distinct reaction set.
- `_task_feasible_without`'s solves ran under Gurobi's default automatic/
  concurrent method, which spins up several threads per solve for a problem
  this small, adding synchronisation overhead across thousands of solves for
  no benefit.
- `find_task_essential_genes`/`find_task_essential_categories` had no way to
  spread the scan's independent gene knockouts across more than one core.

Verified against the previous per-gene scan (sequential and processes=2) on a
synthetic model covering a lone-gene reaction, an isoenzyme pair and a
multi-subunit complex.

* fix: run the knockout-scan pool with a spawn context

`_scan`'s `ProcessPoolExecutor` used the platform default multiprocessing
context, which is `fork` on the Linux CI runners this targets. Forking after
`base`'s Gurobi environment is already running duplicates that environment,
and any internal license/logging threads it holds, into every worker; this is
the same non-fork-safe state that made cobra's default parallel FVA deadlock
on Linux CI runners, which is why `estimateEssentialGenes.estimate_essential_genes`
and `gradedEssentiality.main` both pin FVA to `processes=1`.

- `_scan`: pass `mp_context=multiprocessing.get_context("spawn")` to the pool
  so each worker starts as a fresh interpreter and only creates its own Gurobi
  environment inside `_init_worker`, after the fork boundary, leaving nothing
  shared between workers to deadlock on.

* perf: scan each cell line's gene knockouts on all of the runner's cores

`gradedEssentiality.py` shards cell lines across CI jobs (#1079), but within
one cell line's `find_task_essential_categories` call every gene knockout
still ran on a single core, leaving a shard's other cores idle regardless of
how many the runner had.

- `main`: adds `--processes` (default: `os.cpu_count()`) and forwards it to
  `find_task_essential_categories`, which already supports splitting its
  independent gene knockouts across a process pool.

* chore: update model QC results [skip ci]

* fix: actually update the local branch before the results commit

The gene-essentiality run at
https://github.com/SysBioChalmers/Human-GEM/actions/runs/34445477424 computed
results for all five cell lines (posted as a PR comment) but its "Auto-commit
results" step failed to push: "Updates were rejected because the tip of your
current branch is behind its remote counterpart".

- `gene-essentiality.yml`: "Update local branch before committing changes"
  ran `git fetch` then `git checkout $BRANCH_NAME`, but `git fetch` only moves
  the `origin/$BRANCH_NAME` ref; the local branch stayed at whatever commit
  the job started from. Anything pushed to the branch while the job ran left
  the auto-commit step basing its commit on that stale tip, so its push was
  rejected as non-fast-forward and the finished run's results were lost.

* Edit the /run gene-essentiality comment instead of replying (#1080)

* chore: restore generated model files removed by #1061

* chore: new version

* chore: add the release workflow so it can be dispatched

workflow_dispatch resolves against the default branch, so a Release workflow
that exists only on develop cannot be started. Adding it here makes it
available in the Actions tab.

The workflow calls code/io/increaseHumanGEMVersion.py from the develop
checkout it cuts the release branch from, so a release can only run once
#1078 is on develop.

* ci: edit the /run gene-essentiality comment instead of replying

`/run gene-essentiality` posted its "started" acknowledgement as a separate
new comment, so a PR thread that used the command showed two comments (the
command and the acknowledgement) instead of one.

- `pr-command.yml`: the `gene-essentiality` job's "started" notice is now
  appended to the triggering comment itself (`gh api ... -X PATCH`) rather
  than posted with `gh pr comment`.

* chore: update model QC results [skip ci]

* perf: share Step 1's prepData across shards instead of rebuilding it

Each of the 5 shards in `main` independently called `_prep_human_model_for_ftinit`
whenever it had a pending cell line, even though that step's output does not
depend on which cell line asks for it (it runs before any cell line's expression
data is applied), so the same tens-of-minutes computation ran five times over.

- `gradedEssentiality.py`: adds `_load_or_build_prep`, `--prep-cache` and
  `--prep-only`. `--prep-cache PATH` loads Step 1's `PrepData` from `PATH` if
  present instead of rebuilding it, and writes it there otherwise; `--prep-only`
  builds it and exits without touching any cell line. Without `--prep-cache`, a
  run still builds its own Step 1, as before.

* ci: build the shared prepData once instead of once per shard

The `build` matrix ran `gradedEssentiality.py` for each of its 5 shards without
sharing anything between them, so the newly cacheable Step 1 still ran once per
shard by default.

- `gene-essentiality.yml`: adds a `prep` job that builds `PrepData` once with
  `--prep-only --prep-cache` and uploads it as an artifact; `build` now depends
  on `prep`, downloads that artifact, and passes `--prep-cache` so every shard
  loads the shared prepData instead of rebuilding its own.

* chore: update model QC results [skip ci]

* Partial revert "Edit the /run gene-essentiality comment instead of replying (#1080)"

This partially reverts commit 5883d7e.

* Fix metabolicTasks_Full.txt so it parses with parseTaskList (#1070)

The full metabolic task list was missing the leading (empty) column that
metabolicTasks_Essential.txt has, so RAVEN parseTaskList read the whole file as
a single task. Add the leading column to align the format; parseTaskList now
returns 257 tasks and compareMultipleModels runs against the full list. No task
definitions are changed.

* chore: update model QC results [skip ci]

* Serialize the gene-essentiality build matrix under the WLS session baseline

The five build shards each open a Gurobi session, and --processes previously
defaulted to the runner's core count, opening one more session per worker
process inside each shard. Both ran concurrently against the same
Gurobi_Eduard WLS license, whose session baseline is 2, so the license
server eventually kills sessions once the overage runs too long.

* Split cell-line model-building and essentiality-scanning into separate shards

Each holds its own Gurobi session for the whole step, so running five of one
concurrently already sustains more sessions than the Gurobi_Eduard WLS
license's baseline of 2 for as long as the step takes; --processes further
multiplied that within a single shard's scan. Splitting them into build-model
and build-test lets both still run all five shards at once (no session
survives from one job into the next, so the two 5-way bursts do not merge
into one sustained overage), instead of serializing everything to stay under
the baseline.

* Let build-test run for shards whose build-model succeeded despite others failing

needs: build-model implicitly ANDs an overall success() onto its condition, so
one failed shard (build-model (4)/RPE1 hit the 90-minute job timeout in
34494319341) skipped build-test entirely, even for the four shards whose
context model had already built fine.

* Give build-model room for slow cell lines and report ftINIT progress

build-model's 90-minute timeout was below what one cell line's ftINIT build
can legitimately take: two extraction steps, each up to three solves (the
proven-gap solve plus resolve_ties' two tie-break phases) capped at
TIME_LIMIT = 30 min apiece, i.e. up to 180 min before task gap-filling.
RPE1 needs about 91 min (its first step's proven-gap solve and parsimony
phase both run to the 30-min cap), so its shard was cut off at the end of
extraction in 34494319341 while the other four finished in 9-24 min.
240 min covers the 180-min ceiling with headroom for gap-filling and setup.

The build step also went silent for the whole extraction: ftinit() only
prints its step and gap-fill progress with verbose=True, and optlang sets
OutputFlag = 0 on every Gurobi model it creates, so the global OutputFlag
set by _set_solver_verbosity never reaches the ftINIT MILPs. verbose=True
restores the step/gap-fill lines, and FTINIT_DEBUG=1 adds each solve's
status and achieved gap as it finishes.

Replayed locally for shard 4/5 (RPE1) with the CI's dependency versions
(Python 3.11, gurobipy 13.0.3, cobra 0.32.1, raven-toolbox 61da432):
prep 27 min, build-model 91 min, build-test (--processes 1) 14 min, all
exiting cleanly.

* Fix aggregate job's spurious Gurobi session, add retry and disposal

The gene-essentiality workflow's aggregate job crashed with "Too many
sessions, 5 active sessions for a baseline of 2": cobra prefers Gurobi
automatically whenever gurobipy is importable, so loading the model for
--aggregate-only opened a Gurobi WLS session even though that step never
solves anything and does not need one, and the build-test matrix's 5
just-finished sessions had not yet cleared from the license server.

Force glpk as the default solver before loading the model, letting only
the branches that actually need Gurobi's MILP solver switch to it. Also
add retry-with-backoff around that switch (a sibling shard's session is
far more likely to clear within a minute or two than the license being
genuinely exhausted) and explicit disposal of the default Gurobi
environment at the end of each Gurobi-using branch, so a shard's session
is released to the license server immediately instead of waiting on its
own connection timeout.

* Bound the Gurobi session disposal call with a timeout

disposeDefaultEnv() releasing a WLS session is a network round trip to
the license server, unlike a local/named-user license's instant close.
Running it inline risked an already-finished, already-written-to-disk
shard sitting "in progress" indefinitely if that round trip stalled --
plausibly what the currently running gene-essentiality workflow run is
hitting, since every shard passes through this same code regardless of
how fast its own MILP solve was. Run it on a background thread with a
bounded join instead: a timeout is logged and the process exits anyway,
which releases the session itself, just a little later.

* Add process/resource monitoring and harden Gurobi session retries

The gene-essentiality workflow's job logs are only retrievable after a
job completes, so a currently running (or stuck) build-model/build-test
shard is a black box from the outside -- no way to tell whether it is
genuinely solving or wedged. Add a background resource monitor to both
jobs (CPU/memory/top processes every 60s to a file, uploaded as an
artifact with if: always() so it survives a timeout or failure) to give
visibility into a run while or after it happens, without needing live
log streaming.

Also harden _use_gurobi_solver: a failed session-acquisition attempt
leaves gurobipy's process-wide default environment half-initialised
(the crash is inside its own construction), so best-effort dispose it
before each retry rather than reusing whatever state a broken attempt
left behind. Log how many attempts and how long acquisition took, plus
CPU count and Gurobi's resolved Threads, separating "time spent getting
a session" from "time spent solving" and surfacing possible CPU
oversubscription across the 5 concurrent shards.

* Disable ftINIT's resolve_ties/prove_abs_gap for the gene-essentiality CI check

Both add extra Gurobi solves (a 3-7x and ~2.4x build-time cost respectively)
specifically to pin which of many equally-scoring optima ftINIT returns, for
reproducibility across builds. But at the genome scale and the 1e-9 solver
tolerances this pipeline uses, that pinning is exquisitely sensitive to
floating-point differences between CPU vendors -- and GitHub-hosted
ubuntu-latest runners are not hardware-homogeneous: three consecutive
workflow_dispatch runs of an isolated benchmark just drew an Intel Xeon
Platinum 8573C, an AMD EPYC 7763, and an AMD EPYC 9V45. Raw CPU and Gurobi
throughput were healthy on all three, so this isn't a runner/license
slowdown; it's the extra tie-break/proven-gap solves turning "expensive but
bounded" into occasional multi-hour solves that blow through TIME_LIMIT
depending on which chip a run happens to land on.

Turning both off trades exact cross-build reproducibility (which the CI
check doesn't need -- it just needs to confirm the pipeline still runs and
predictions are still reasonable) for being robust to that hardware
lottery. Tolerances are being looked at separately.

* Re-enable PROVE_ABS_GAP; keep RESOLVE_TIES off

Dropping both in 756a56d removed the tie-break overhead but exposed a worse
failure mode: without PROVE_ABS_GAP, ftinit() falls back to the relative-gap
escalation, an open-ended loop of full-TIME_LIMIT re-solves rather than a
single bounded one. On PR #1069 (run 35517420976, 2026-09-20) that took over
an hour per extraction step; with five shards holding Gurobi sessions that
long concurrently, two tripped the WLS license's "Overage for too long" kill,
and the crashed (not gracefully closed) sessions then poisoned the
downstream build-test jobs for the rest of the run.

PROVE_ABS_GAP's single proven-gap solve costs ~2.4x a quick solve but has a
known ceiling (TIME_LIMIT), unlike the escalation's TIME_LIMIT-times-unbounded-
rounds. RESOLVE_TIES stays off since its tie-break phases are pure additional
cost on top of the base solve.

* Cap build-model at 2 concurrent shards to match the WLS session baseline

The build-model/build-test split assumed each 5-way-parallel burst was short
enough that sustained over-baseline usage wouldn't matter. It isn't: a cell
line's ftINIT build now commonly takes 1-2+ hours, so five shards each
holding a Gurobi session that long is a sustained 5-session stretch far past
the Gurobi_Eduard WLS license's baseline of 2. On PR #1069 (2026-09-20/21)
this reliably killed shards outright ("Overage for too long") and, once one
crashed mid-solve instead of releasing its session cleanly, poisoned other
shards' sessions for the rest of the run too ("Maximum number of failing
server authorization attempts reached") -- reproducing the original
aggregate failure this workflow started from, twice, across two different
attempts to fix it via solve-time settings alone.

max-parallel: 2 caps build-model at the WLS baseline directly: at most 2
sessions are ever open at once, independent of how long any individual
shard's solve takes. Same shard count and SHARD_COUNT (still one shard per
cell line), just throttled concurrency.

* chore: add gene essentiality test result [skip ci]
* fix: couple non-ETF flavoenzymes to ubiquinone instead of the ETF pathway (#1015)

MAR06911 (EC 1.5.5.1) represents the ETF:ubiquinone oxidoreductase (ETFDH)
step that reoxidises the matrix FADH2 pool. Its gene rule had accumulated
into an 'or' of nine genes, six of which encode flavoenzymes that reduce
ubiquinone directly and do not use ETF: GPD2, PRODH, PRODH2, CHDH, DHODH
and SQOR (Banerjee et al. 2022, FEBS J, PMID:34428349). This caused two
artefacts, most visible in enzyme-constrained (GECKO) models:
- all FADH2-to-ubiquinone flux was constrained by the single ETFDH kcat,
  so e.g. the glycerol-3-phosphate shuttle was constrained twice;
- beta-oxidation electrons could reach the ETC without ETFDH (via the GPD2
  branch of the 'or' rule), which is biologically incorrect.

Changes:
- Restore MAR00483 (sn-glycerol-3-phosphate:ubiquinone oxidoreductase,
  GPD2, EC 1.1.5.3); remove FAD-linked MAR00449 and its Recon3D duplicate
  MAR01169.
- Restore MAR03838 (L-proline:ubiquinone oxidoreductase, PRODH/PRODH2);
  remove FAD-linked MAR08611 (PRODH electron transfer to ubiquinone:
  Tanner 2018, PMID:28990412).
- Set the MAR06911 gene rule to the ETF system only,
  "ENSG00000105379 and ENSG00000140374 and ENSG00000171503"
  (ETFB and ETFA and ETFDH), matching the and-joined convention used for
  the other obligate ETC complexes.

DHODH already reduces ubiquinone through its own reaction (MAR20167) and
needs no further change. CHDH (MAR08441) and SQOR (MAR20186) still deposit
electrons in the matrix FADH2 pool and should be given direct ubiquinone
reactions in a follow-up. Removed reactions are recorded in
deprecatedReactions.tsv.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* fix: remove orphaned mitochondrial DHAP node after GPD2 recoupling

Removing the FAD-linked MAR00449 left MAM01690m (mitochondrial DHAP)
without a producer, since the restored ubiquinone-linked GPD2 (MAR00483)
releases cytosolic DHAP. The metabolite and its only remaining reaction,
the export transport MAR08111, are a MACAW dead-end and are removed and
moved to the deprecated identifier lists.

Drop the stale MAR00483 and MAR03838 rows from deprecatedReactions.tsv,
as those identifiers are active reactions again after this PR.

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

* Scope the gene-essentiality PR comment to genes a change could affect

The existing summary table aggregates predictions across ~3000 genes against
Hart 2015, which is dominated by ftINIT's run-to-run noise and does not say
whether a specific model edit made things better or worse.

- `diffGeneEssentiality.py`: diffs the already-committed gene-essential.csv
  against the target branch's own copy, scoped to genes in the reactions a
  diff changed plus genes one metabolite hop away, keeping only flips that
  agree in direction across most cell lines. A flip in whether growth is
  blocked is checked against Hart 2015 and labelled correct or wrong; a flip
  in some other task capability is reported but not scored, since Hart only
  measures growth.
- `gene-essentiality.yml`: runs the scoped diff after aggregating results and
  folds it into the PR comment, with the per-gene detail as a workflow
  artifact.
- `pr-command.yml`: passes the pull request's base branch through to the
  dispatched workflow so it has something to diff against.

* Group identical rows in the gene-essentiality diff table

Several genes commonly change for the same reason (all three ETF complex
subunits here), and one row per gene just repeats the same cells; group genes
sharing the same lines/role/change into a single comma-joined row instead.

* Replace the global essentiality table in the PR comment, not append to it

The scoped diff section was being added alongside the full ~3000-gene table
instead of instead of it, defeating the point: that table is dominated by
ftINIT noise in genes the PR never touched. Show the scoped section when a
base to diff against is available, falling back to the full table only when
it is not (manual dispatch, or no gene-essential.csv on the target branch yet).

* Tighten formatting in the scoped gene-essentiality report

Bold the reaction/gene counts so they read at a glance, italicize the
consensus-threshold explanation, fold the likely-noise gene list behind
details instead of listing it inline, name the actual base branch instead of
a generic placeholder, and link the committed gene-essential.csv instead of
leaving it as plain text.

* Collapse unchanged checks in the model quality report

Every row rendered on every run regardless of status, so a clean pull request
still posted 18 always-expanded rows of zeros and pre-existing findings. Merge
the model-checks and MACAW/balance tables (the split was already noted as
arbitrary), fold clean and pre-existing rows behind one details block since
neither is this pull request's problem, and keep only a new regression or a
still-running check inline. The two merge gates get a one-line summary next
to the verdict so they stay visible without opening anything. Drop the
per-row README link (rarely used, most of the raw markup) in favor of one
link at the top; keep the per-value CSV link where there is a finding to
look at. The metabolic-tasks table (every row there is a gate) compacts to
one line when everything passes instead of folding.

* chore: update model QC results [skip ci]

* Redesign the model quality report: one row per section, not per check

The folded 18-row table still duplicated the full breakdown into the comment
on every run; nothing about a clean or pre-existing check needed to be there
at all, since the point of comparing against base is that unchanged is not
this pull request's problem. Split into two files: model_qc_summary.md keeps
every individual check, unabridged, and is what the comment now links to;
model_qc_comment.md is the new condensed report actually posted -- one row
per section (network checks, file/task gates, MEMOTE, gene essentiality)
naming only the checks that need attention, with everything else folded into
that row's count. The two merge gates stay in the verdict line itself.

* Redesign the gene-essentiality report: one row per category

The two mini-tables plus a folded 32-gene noise list still put every gene's
name in the comment somewhere. Replace with one table, one row per category
(growth effect changed, other role changed, likely noise, no change) --
gene names stay only for the two small, actionable categories; noise and
unchanged genes are counted, never named, since nobody needs 32 or 397 names
in a comment. The per-gene detail CSV is now committed to
data/testResults/gene-essential-diff.csv alongside gene-essential.csv itself,
replacing the 30-day workflow artifact with a permanent, linkable file.

* chore: update model QC results [skip ci]

* Add an improved icon and tighten wording in the model quality report

A count that fell since base, a score that rose, or growth that got fixed
all rendered as the same clean/warning icons as "nothing changed", so a
genuine improvement from this pull request was invisible. Add a distinct
icon (:sparkles:) for that case, in both _icon() and its own "Improved:"
callout. Also: drop the "Gene essentiality" row (that result lives in its
own comment now, not here); add a "Full report" row linking model_qc_summary.md
instead of a separate sentence below the table; "5" instead of "5 gates";
bullet points instead of a comma-joined sentence for the pre-existing list,
each with a colon before its (now bolded) count.

* Move gene names out of the comment, add an improved icon, drop /reevaluate

The condensed table still put every gene's name and what changed for it in
the Genes/Detail columns. Replace with a single Status column (icon and
count only, matching the model-quality report's style); the per-gene detail
that used to sit there is now appended to the committed
gene-essential_summary.md instead, with the comment linking to it. Growth
effect changed also gets :sparkles: when Hart 2015 agrees with the flip,
distinct from the plain check used for "no change" elsewhere. Dropped the
reaction-change counts from the lead sentence (kept the gene counts) and
removed the /reevaluate gene-essentiality command: it fully overwrote
gene-essential_summary.md, which would have silently dropped the appended
section on the next run.

* chore: add the scoped gene-essentiality diff for this change

Computed with diffGeneEssentiality.py against the already-committed
gene-essential.csv on both develop and this branch -- no ftINIT/Gurobi
re-run needed. Appends the per-gene breakdown to gene-essential_summary.md
and adds the full per-gene, per-line CSV, matching what the gene-essentiality
workflow will write on its next real run.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
#1082)

* fix: build the ftINIT MILP on Gurobi in the gene-essentiality driver

The GLPK default set at the start of main() stayed in place after the template
model was switched to Gurobi, so prepINITModel's merged min_model, which cobra
builds from scratch, got GLPK, and ftINIT's MILP ran on it. Switching a model to
Gurobi now also makes Gurobi the global default, and Step 1 logs the MILP's
solver backend.

* test: resolve ftINIT ties in the gene-essentiality builds

With RESOLVE_TIES on, the same prepData loaded onto Gurobi by two routes gives
identical context models for all five Hart2015 cell lines, where without it they
differed by 4-9 reactions. Summed build time goes from about 92 to 124 min.

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

Produced outside GitHub Actions with the same code and pinned stack (raven-toolbox
98d02a8, gurobipy 13.0.3, cobra 0.32.1, Python 3.11): prep, one build and one scan
per cell line, then gradedEssentiality.py --aggregate-only and the workflow's
diffGeneEssentiality.py step. With RESOLVE_TIES on, context models built this way
match those built by GitHub Actions reaction for reaction on all five cell lines.

* chore: scope the gene essentiality diff to every gene [skip ci]

This pull request changes the algorithm rather than the model, so the diff against
develop covers all 2848 genes in the matrix instead of the genes of changed reactions.
… reactions (#1081)

* fix: route SQOR and CHDH through ubiquinone, drop PRODH2 from proline reactions

Follow-up to #1028, addressing the three refinements confirmed in the last
comment on #1015 that #1028 itself didn't include.

- `MAR20186` (SQOR): wrote its electrons into the matrix FADH2 pool even
  though its EC (1.8.5.8) is a direct quinone reductase, so once #1028
  restricted `MAR06911` to the ETF complex only, SQOR's electrons would have
  had no route to the ETC at all.
- `MAR08441` (CHDH): same FADH2-pool problem for the same reason -- a
  purified bacterial homolog shows physiological turnover with quinone as
  acceptor, and ETF-deficiency disorders show choline accumulation rather
  than accumulation of CHDH's own product, both against a shared-FAD model.
- `MAR03838` and `MAR08610`: PRODH2's gene rule on both proline reactions
  assumed it shares PRODH's activity, but PRODH2 is a hydroxyproline
  dehydrogenase (no hydroxyproline reaction exists in this model) with a
  turnover on proline too low to carry physiological flux, and neither
  PRODH2 knockout nor overexpression affects proline levels the way PRODH's
  does. `MAR08610`'s only gene was PRODH2, so removing it leaves no valid
  gene for that reaction; deprecated accordingly.

* fix: give PRODH2 its own ubiquinone-linked hydroxyproline reaction

PRODH2 (ENSG00000250799) is a mitochondrial FAD-dependent hydroxyproline
dehydrogenase (EC 1.5.5.3) that reduces ubiquinone directly, not the
proline-oxidizing, NAD(P)+-linked activity it was previously attributed
with on MAR03838/MAR08610 (PMID:25697095). The model already has the rest
of the pathway: MAR09002 transports hydroxyproline into the mitochondrion,
and the product this reaction feeds, L-1-pyrroline-3-hydroxy-5-carboxylate
(MAM02320m), already has four downstream consumers -- so this does not
introduce a new dead end, it gives PRODH2 a route into a part of the
network that was previously only reachable via PYCR1/PYCRL/PYCR2 running
in reverse.

* chore: add KEGG/Rhea cross-references for MAR20192

KEGG R03295 confirmed directly from KEGG's own EC 1.5.5.3 entry. Rhea
master RHEA:52512 confirmed independently via UniProt's PRODH2 catalytic-
activity annotation and a direct Rhea search hit for the same equation.
RHEA:52513 (the directional entry) follows the rxnRheaID = rxnRheaMasterID
+ 1 pattern every other row in this file uses, but is not independently
confirmed since rhea-db.org blocked direct fetches.

* fix: stop counting one-direction reversible reactions as MACAW dead ends

MACAW marks a reversible reaction that structural propagation limits to one
direction as `only when going forwards` or `only when going backwards`. The
model quality report counted these with the true dead ends, so a change that
made a reversible reaction directional (for example removing the only
irreversible producer of a metabolite) raised the dead-end count and showed a
regression even though the reaction can still carry flux. Only true dead ends
are counted now; the per-reaction values stay in macaw_results.csv.

* Write only MACAW findings, as a TSV

macaw_results.csv listed all 12,875 reactions, of which 10k+ passed both
tests. The file is now macaw_results.tsv and holds only reactions with a true
dead end or a duplicate flag (1,518 rows); reversible reactions merely limited
to one direction are not findings and are omitted. The report's dead-end and
duplicate counts are unchanged (1,141 and 377); the base-branch fetch lists
and the README follow the new name.

* chore: add gene essentiality test result [skip ci]

Context models and knockout scans are those of GitHub Actions run 35901385430,
whose model and code are unchanged by the rebase onto develop; the scoped diff is
regenerated against develop's results from #1082.

* chore: add gene essentiality test result [skip ci]
)

* fix: correct SAM synthesis GPR and remove non-catalytic MAT2B (#1013)

MAR03875 (SAM synthesis) and MAR07139 (SeAdoMet synthesis) had the GPR
"MAT2B or MAT1A or MAT2A", which incorrectly lets MAT2B catalyse the
reaction on its own. MAT2B (ENSG00000038274) is a non-catalytic
regulatory subunit: it stabilises MAT2A and lowers the Km for methionine
and the Ki for SAM, but its knockout leaves cellular SAM levels unchanged
while only MAT2A depletion reduces SAM (PMID:39353892). MAT2A forms a
catalytically active homotetramer without MAT2B and is the catalytic
subunit; MAT1A is the catalytic isozyme.

Set both GPRs to "ENSG00000151224 or ENSG00000168906" (MAT1A or MAT2A).
MAT2B was associated only with these two reactions, so it is removed
from the gene list.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* ci: overwrite the /run gene-essentiality comment with the results

* chore: add gene essentiality test result [skip ci]

* chore: add the scoped gene-essentiality diff for this change

The /run gene-essentiality run was dispatched without a base branch, so it
skipped the scoped diff and posted the unscoped table. Computed with
diffGeneEssentiality.py from the committed gene-essential.csv on develop and
on this branch (no re-run); appends the per-gene breakdown to
gene-essential_summary.md and replaces the stale gene-essential-diff.csv
inherited from #1028 with this change's own.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: export each metabolite's InChI from metabolites.tsv

annotateGEM.py merged the database identifiers from metabolites.tsv but not
metInChI, so the Excel InChI column and the SBML took the YAML's inline inchis
field instead: 1063 values, 417 of them in the non-standard InChI=1/ form and 479
differing from the curated metabolites.tsv. The metInChI column now fills
notes['inchis'] (Excel InChI column) and annotation['inchi'] (SBML), as
annotateGEM.m does, and the YAML no longer carries its own InChIs. A trial export
gives 6409 InChIs in both the .xlsx and the .xml.

* fix: add and re-protonate metabolite structures to match formula and charge

- 81 SMILES/InChI pairs re-protonated to the state whose formula and charge
  match the model (dimorphite-dl enumeration; only protons change, the InChIKey
  connectivity and stereo blocks are unchanged).
- 8 compartment copies without a structure take it from another compartment of
  the same metabolite with identical formula and charge (5-deoxyadenosine, FMN,
  hexanoate, FMNH2).
- 2 new structures from MetaNetX whose protonation state matches the model:
  [2Fe-2S] cluster (MAM20085m) and S-sulfanylglutathione (MAM20086m).

Structure/formula-charge inconsistencies: 397 -> 316.

* chore: update model QC results [skip ci]
* feat: add ModelSEED reaction and compound cross-references

- metSeedID: fill 721 empty cells. Matched by InChIKey (protonation-insensitive,
  exact protonation preferred, core ModelSEED compounds preferred), via
  MetaNetX, and for metabolites without a concrete structure by exact name.
  Existing ids are kept.
- rxnSeedID: new column in reactions.tsv with 2881 reactions, matched by
  stoichiometry over the mapped compounds (H+ kept when it crosses a membrane)
  or via MetaNetX when the ModelSEED reaction shares the compounds and the
  transport type.
- annotateGEM.py/.m map rxnSeedID to seed.reaction; annotationTest.py checks
  the metSeedID/rxnSeedID formats and metSeedID across compartments;
  verifyAnnotations.py knows rxnSeedID.

* fix: correct three existing ModelSEED compound ids

hydroxide pointed to water (cpd00001) and D-ribulose to the generic Ribuloses
(cpd28115); hydrochloride pointed to chloride (cpd00099), and ModelSEED has no
HCl entry.

* chore: update model QC results [skip ci]

* fix: correct four ModelSEED compound ids to the model's compound

Resolved against the model's own KEGG id and ModelSEED's KEGG aliases:
- (3S)-citramalyl-CoA: cpd00671 (unspecified, C00904) -> cpd00743 (C01011)
- L-2-amino-3-oxobutanoate: cpd02053 (C03214) -> cpd02211 (C03508)
- L-erythro-4-hydroxyglutamate: cpd01974 (C03079) -> cpd19042 (C05947)
- Pectin: cpd00280 (D-galacturonate, the monomer) -> cpd11601 (Pectin, C00714)
* fix: add and correct reaction cross-references (KEGG, BiGG, MetaNetX, Rhea)

New ids come from matching each reaction's structural signature to MetaNetX
(participants by InChIKey, H+ kept only when it crosses a membrane, BiGG ids
restricted to the reaction's own compartments), and from each metabolite's BiGG
id for exchange reactions (EX_<id>_e).

Existing ids were verified against the reaction they point to in MetaNetX.
An id is corrected when all participants have a structure and they differ
(skeleton, MetaNetX id, name and heavy-atom formula all disagree), when it is a
BiGG id for other compartments, when a same-formula participant is a different
isomer (reviewed by name), or when the KEGG reaction no longer exists. Ids that
differ only in cofactors (NAD(P), FAD, FMN, O2/H2O2, quinones, ATP/ADP/AMP,
CO2/HCO3-) or that cannot be verified are kept.

Added: BiGG 1176, MetaNetX 1067, Rhea (master) 888, KEGG 6.
Replaced: BiGG 624 (181 misaligned exchange ids), MetaNetX 40, KEGG 6.
Removed: BiGG 312, KEGG 78 (28 no longer in KEGG), Rhea 36 (+8 directional
ids of the same reaction), MetaNetX 35.

* chore: update model QC results [skip ci]

* fix: add and correct metabolite cross-references

Existing ids were verified against the compound they point to (MetaNetX, and
PubChem by InChIKey) and against the source databases for ids MetaNetX lacks.

- PubChem: 1037 CIDs added by InChIKey (neutral form); 7 CIDs of a different
  compound replaced (e.g. 7alpha-hydroxycholesterol had 20alpha-, chloride had
  chlorine, deoxyribose the L-enantiomer).
- Ids that no longer exist in their database: 78 replaced by the current id
  (LipidMaps acyl-CoAs, ChEBI numbers that are not ChEBI ids, obsolete
  CHEBI:17208, KEGG C00425 -> C05422 and others), 6 LipidMaps ids removed.
- Wrong compound: guanidine, acetanilide and a tridecanoyl lysophosphatidate had
  the MetaNetX id of another compound; isocaproic aldehyde the LipidMaps id of
  hexanal; 3-ketopristanoyl-CoA the HMDB id of pristanoyl-CoA; C16:1-ethanolamide
  all ids of the C16:0 compound.
- 69 ids added for metabolites without a concrete structure, from their
  MetaNetX id where its name matches.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]
…AR11311 (#1033)

* feat: associate Ca2+ influx channels with MAR11311 (#1001)

MAR11311 (Ca2+[e] -> Ca2+[c]) was labelled passive diffusion with no GPR,
but uncoupled Ca2+ entry is channel-mediated. It is the only uncoupled
Ca2+ influx reaction in the model (the other extracellular/cytosol Ca2+
reactions are antiporters and ATPases). Because every plasma-membrane
Ca2+ influx channel catalyses the identical Ca2+[e] -> Ca2+[c] reaction,
separate per-channel reactions would be duplicates, so they share this
reaction's GPR.

- Set the GPR of MAR11311 to the voltage-gated (Cav1/2/3) and store-
  operated (ORAI) Ca2+ channel pore-forming subunits: CACNA1A, CACNA1B,
  CACNA1C, CACNA1D, CACNA1E, CACNA1F, CACNA1G, CACNA1H, CACNA1I, CACNA1S
  and ORAI1, ORAI2, ORAI3 (13 genes, joined by OR).
- Rename MAR11311 from "...via Diffusion..." to "...via Ion Channels...".
- Add the 13 genes (all new to the model) to the gene list and genes.tsv.

The genes.tsv rows carry gene identity only (Ensembl ID, symbol, name);
their transcript/protein/UniProt/Entrez annotations and localisation
should be populated by code/GPRs/fetch_ensembl_gene_annotations.py, the
standard generator for that file. Exported model files regenerate at
release.

Expands the L-type request in #1001 to the other voltage-gated and
store-operated channels that share the same reaction.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]
ci: start the slowest gene-essentiality shards (GBM, RPE1) first
ci: always post the scoped gene-essentiality comment
…1038)

* fix: remove LDHAL6A from MAR11479 GPR for consistency with MAR11476 (#528)

MAR11476 and MAR11479 are the same promiscuous lactate dehydrogenase
side-reaction on adjacent branched-chain 2-oxo acids. The GPR curation in
#847 / #885 removed LDHAL6A (ENSG00000166800) from MAR11476 as not
relevant to the reaction, but left it on MAR11479, leaving the two
inconsistent. Remove LDHAL6A from MAR11479 as well so both carry the same
five-gene rule (LDHB, LDHA, LDHC, LDHD, LDHAL6B).

* chore: add macaw test result

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci: show which MEMOTE run the score comes from, and the full-suite score when current

* chore: update model QC results [skip ci]
* feat: add cellular succinate export via MCT proton symport (#424)

Human-GEM has three Na-coupled succinate import reactions across the
plasma membrane (MAR09610, MAR04939, MAR04938) but no export, so
cytosolic succinate could not be secreted. Succinate export is relevant
in hypoxia and is mediated by the monocarboxylate transporters MCT1
(SLC16A1) and MCT4 (SLC16A3) (PMID:35363247).

Add MAR20192, a reversible succinate + H+ symport across the plasma
membrane (GPR SLC16A1 or SLC16A3), modelled like the existing lactate
MCT reaction MAR05998. Secretion now works through the existing exchange
reaction MAR09415. Mitochondrial succinate export is already possible via
the reversible SLC25A10/SLC25A1 antiporters, so no mitochondrial reaction
is added.

* chore: add macaw test result

* chore: update model QC results [skip ci]

* chore: full MEMOTE result [skip ci]

* fix: make MAR20193 succinate transport export-only

* chore: add gene essentiality test result [skip ci]

* chore: update model QC results [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…1098)

* ci: test growth on a capped Ham's medium instead of the default bounds

* ci: show MEMOTE per-test scores, not raw metrics, and leave out skipped tests

* ci: copy model QC results onto the head branch instead of stash-merging them

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]
* fix: balance MAR20189, the ThDP intermediates and the H-protein lipoyl cycle

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]
…1101)

* feat: flag diffusion transports as spontaneous so ftINIT keeps them

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

* ci: count correct and wrong essentiality changes separately in the summary

* chore: update model QC results [skip ci]
* feat: add PXMP2 as the peroxisomal pore for small-solute transport

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: flag non-enzymatic reactions as spontaneous

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…etaNetX ids (#1090)

* fix: one base id per metabolite, and correct cytochrome c and water MetaNetX ids

- 14 metabolites existed under two base ids in different compartments (same
  name, structure and cross-references): biomass (MAM03971e -> MAM03970e) and
  13 metabolites added in the MAM200xx range, e.g. 1,4-benzoquinone
  (MAM20009c -> MAM20007c). Each second id is renamed to the first base id with
  its own compartment and added to the deprecated metabolites.
- Ferricytochrome c, ferrocytochrome c and cytochrome c all had MNXM5749, which
  MetaNetX no longer has: now MNXM731952, MNXM731957 and MNXM731958, the
  MetaNetX entries of their KEGG ids C00125, C00126 and C00524.
- H2O gets MetaNetX's id WATER; annotationTest.py accepts it.

* chore: update model QC results [skip ci]

* fix: MetaNetX ids of glycogenin (apo) and hydrogen chloride

* fix: merge 37 duplicate metabolites and the reactions they made identical

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* fix: MAR08607 is glutamine transaminase K, producing 2-oxoglutaramate (charge-imbalanced as glutamate)

* revert: keep memoteSnapshot.py as on develop

* ci: run model QC check groups as parallel jobs, all on GLPK

Seven check groups (structural, annotation, MACAW, balance, model file,
metabolic tasks, MEMOTE core subset) run as a matrix. A report job merges
their result files and changed qc_status.tsv keys, commits once and posts
the comment. The MEMOTE core subset now solves with GLPK, so model QC
never uses a Gurobi licence session; the full suite keeps Gurobi.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#1040)

* fix: remove charge-imbalanced duplicate lipoxygenase reactions (#348)

Five arachidonate/DHA peroxidation reactions are 1:1 duplicates of
balanced multi-stoichiometry reactions that produce the same
hydroperoxide (same substrate and product, no GPR), but with
charge-imbalanced stoichiometry. Remove the imbalanced 1:1 versions and
keep the balanced counterparts:

  removed    kept       product
  MAR01364   MAR03880   12-peroxy-eicosatetraenoate
  MAR01355   MAR03882   8-peroxy-eicosatetraenoate
  MAR01358   MAR03878   9-peroxy-eicosatetraenoate
  MAR01361   MAR03884   11-peroxy-eicosatetraenoate
  MAR01405   MAR03261   10-peroxy-docosahexaenoate

Each product is still produced by its balanced counterpart, so the model
stays balanced. Removed reactions moved to deprecatedReactions.tsv.

The deeper cause (hydroperoxide and hydroxy fatty-acid metabolites that
are one H short, affecting ~105 reactions) is tracked in a separate issue.

* chore: update model QC results [skip ci]

* fix: load models with GLPK in diffGeneEssentiality, which solves nothing

The scoped diff loads the base and head models only. With gurobipy installed,
cobra made Gurobi the default solver, so each load took a WLS licence session;
straight after the build-test shards these can all still be held, and the diff
failed with "Too many sessions".

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ions (#1041)

* fix: correct mechanism and stoichiometry of duplicate transport reactions (#347)

Resolves the remaining similar-reaction pairs from #347, which split into
distinct mechanistic groups rather than a single "2 vs 1 ATP" problem.

Vesicular secretion (SLC18A1/A2/A3, SLC32A1): these transporters are H+
antiporters, but their gradient is across the secretory-vesicle membrane,
which the model does not represent, so the ATP acts as a proxy for the
V-ATPase loading cost. Standardised to 1 ATP per molecule (Recon3D form),
removed the inconsistent HMR 2-ATP-per-3-molecule duplicates, and added a
rxnNotes comment. Kept MAR01442,00052,00065,00334,01555,00058,00087,01396,
01314,01518; removed MAR07633-37,07681,07736-38,08922.

Na+-multivitamin symport (SLC5A6/SMVT, biotin, pantothenate): removed the
spurious ATP and modelled as Na+ symport, 2 Na+ : 1 substrate
(PMID:25809983). Kept MAR00094, MAR01636; removed MAR07667, MAR08930.

Na+/Cl- symport (SLC6A12/BGT1, betaine, GABA): set coupling to
3 Na+ : 1 Cl- : 1 substrate. Kept MAR09873, MAR09886; removed MAR09603,
MAR09604. The GAT reaction MAR09613 (2 Na+ : 1 Cl-) is a distinct
transporter and is unchanged.

Bile acid / bile-acid-CoA transport (ABC transporters, 2 ATP): kept the
HMR 2-ATP form, removed the Recon3D 1-ATP duplicate. Kept MAR01702,
MAR01857, MAR01861; removed MAR02283, MAR02290, MAR02298.

Sterol 4-methyl oxidation (MSMO1): kept MAR00116 (3 NADPH/3 O2, the correct
3-step monooxygenation), removed MAR02300 (1 NADPH/2 O2).

Removed reactions moved to deprecatedReactions.tsv. The sulfate pair
(MAR01913/MAR02304) is left for a closer look, as its mechanism is not
clearly ABC. All modified reactions remain mass and charge balanced; no
genes are orphaned.

* chore: update model QC results [skip ci]

* fix: merge the SMVT symports MAR00094 and MAR01636 into MAR09609 and MAR09607

With the Na+ symport corrected to 2 Na+ : 1 substrate, MAR00094 (biotin) and
MAR01636 (pantothenate) became identical to the HMR reactions MAR09609 and
MAR09607. The HMR reactions are kept: the BiGG and MetaNetX ids of MAR00094
and MAR01636 describe the ATP-coupled form, while MNXR158886 on MAR09609 is
the Na+ symport. Their references move to the kept reactions, which are
renamed to describe the symport.

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: enable collagen secretion and remove duplicate MAR09553 (#335)

The collagen synthesis pathway (MAR06983 -> MAR06984 -> MAR06985) ended
at the dead-end metabolite 1,2-D-glucosyl-5-D-(galactosyloxy)-L-lysine-
procollagen (MAM00232c), so it could not carry flux. MAR09553 was a
redundant duplicate of MAR06983 (same product, same GPR) whose substrate
procollagen-L-lysine (MAM02768c) was itself a dead-end input.

- Remove MAR09553 and its orphaned dead-end substrate MAM02768c
  (moved to the deprecated lists).
- Add secretion of the final procollagen precursor: MAM00232e, a
  transport MAR20192 (MAM00232c -> MAM00232e) and an exchange MAR20193,
  modelled like the existing hyaluronate secretion, giving the collagen
  pathway a sink so it can carry flux.

A general secreted-protein-export framework and collagen proline
hydroxylation are out of scope for the generic Human-GEM and left for
context-specific / specialised models.

* chore: add QC test results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
edkerk and others added 9 commits September 27, 2026 22:19
…f those found (#1095)

* fix: check and correct reactions split across compartments

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…omega-hydroxylation (#1096)

* fix: mitochondrial GPRs and FAD for vitamin E beta-oxidation, CYP4F2 for its omega-hydroxylation

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
#1085) (#1104)

* fix: metabolite identity, structure and formula inconsistencies from #1085

- The 2-oxoadipate/BCKDH thiamine adduct gets its own id, MAM20089m, since
  MAM20077 is 2-O-acetyl-ADP-D-ribose; the copied KEGG and MetaNetX ids of
  that compound are dropped.
- Structures that differed between compartments of one metabolite now match
  the fully specified one (elaidate, 15(R)-HEPE, all-trans-decaprenyl
  diphosphate, 3-methylhistidine, lysophosphatidylethanolamines and others).
- MAM03652c is C16:1-ethanolamide, as in [e], and its hydrolysis (MAR20093)
  gives palmitoleate.
- MAM00270 is 20-HETE (structure, KEGG, LIPID MAPS and its reactions); it was
  named 10-HETE.
- H2S has the HS- structure the model uses; (2R)/(2S)-pristanic acid and
  pristanal get their (6R,10R) structures.
- Cholesterol ester formula uses R, not X.

* fix: curate metabolites whose structure contradicts their formula (#1085)

Each metabolite flagged by the structure consistency check, other than a plain
proton shift, was checked against its name, its reactions and its
cross-references:

- Structure of a different compound replaced (e.g. 2-bromopyridine on an
  epoxy-alkoxyl radical, 3-hydroxybutyrate on (2R)-pristanoyl-CoA, 11-cis-
  retinyl palmitate on 11-peroxy-DHA, oxypurinol on alloxan, neutral atoms on
  chloride and K+), and the cross-references that belonged to that compound
  removed. Lipid peroxy intermediates get peroxyl radical structures, as
  their formulas and reactions require.
- Formula or charge corrected where the structure was right: the
  lysophosphatidylcholines and sphingomyelins (zwitterions, one H less),
  paclitaxel and 6-hydroxypaclitaxel (20 H), insulin, somatostatin, peptide
  charges, methylcobalamin, and the eicosanoid charges that differed between
  compartments.
- Structures removed from polymers, protein-bound glycans and the dolichol
  family, whose chain length varies.
- MAR01070, MAR05427, MAR06350, MAR08993 and MAR12993 get the reactants their
  corrected metabolites need to balance.
- His-Gly-Lys, docosatrienoic acid and docosatetraenoylcarnitine renamed to
  match their reactions and structures.

No reaction becomes imbalanced; 12 become balanced. The structure check drops
from 315 to 71 rows, mostly plain proton shifts.

* chore: update model QC results [skip ci]

* fix: MAR20093 hydrolyses palmitoylethanolamide; ThDP adduct and O-acetyl-ADP-ribose ids; review corrections

MAR20093 is NAAA's palmitoylethanolamide hydrolysis (RHEA:45064), and its
substrate was added in 2023 as palmitoylethanolamide (CHEBI:71464) under the
C16:1-ethanolamide base id. It now uses MAM03885c (palmitoylethanolamide)
and gives palmitate again; MAM03652c is deprecated into it.

- KEGG C22667 and MNXM37367 are the ThDP adduct and move to MAM20089m, which
  records MAM20077m as retired; 2-O-acetyl-ADP-ribose (MAM20077n) gets KEGG
  C20658 and MNXM732228.
- Dopachrome o-semiquinone has the L (2S) configuration of L-dopachrome.
- Chloride uses MNXM735978 (MNXM43 is deprecated in MetaNetX).
- ModelSEED ids of Leu-Gly (cpd32743) and C16:1-ethanolamide (cpd40310)
  corrected; those of GalN-6-P, oxypurinol and retinyl palmitate removed from
  galactosamine, alloxan and 11-peroxy-DHA.
- LPE(18:2) PubChem 90657442 (9Z,12Z); ubiquinol-10 CHEBI:64183.
- Pristanoyl-CoA (MAM03887) leaves C2 undefined, as a generic metabolite.

* chore: update model QC results [skip ci]

* fix: merge MAM20017n into MAM20015 (13,14-dihydro-15-oxo-PGE2)

MAM20015g and MAM20017n have the same name, structure, formula, charge and
cross-references (CHEBI:57402, MNXM733372) in disjoint compartments, a pair
from #626 that #1090 missed. The nuclear one becomes MAM20015n and records
MAM20017n as retired.

* fix: MAR20019 reduces 15-oxo-PGE2; deprecate the free-cobalamin methionine synthase MAR03138

- MAR20019 is PTGR3's 15-oxoprostaglandin 13-reductase (UniProt Q8N4Q0,
  RHEA:11912), written in the NADPH-reducing direction with a single gene.
- MAR03138 methylated free cob(II)alamin with SAM, duplicating the MTRR
  reactivation of methionine synthase already modelled with the enzyme-bound
  cofactor (MAR09550); its product methylcobalamin had no other reaction.
  Both are deprecated.

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

* fix: peptide charges follow the model convention; Asp/Glu peptides named correctly

Lys-Arg-Leu is +2, Pro-His-Tyr and Trp-Pro-Gly are 0 (termini as
zwitterion, charge from the side chains), with the H+ of their hydrolysis
reactions adjusted. MAM03464, MAM03606 and MAM03607 are the Asp-Ala-Arg,
Glu-Arg-Leu and Glu-Asn-Leu that their structures, formulas, reactions and
Recon3D ids describe.

* chore: update model QC results [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
)

* fix: corrections to #1039, #1041, #1040, #1090, #1028, #1099 and #1038 from the curation review

- MAR20193: MCT1 carries succinate with 2 H+ (UniProt P53985, RHEA:29303,
  PMID:32946811); MCT4 has no succinate evidence, so the GPR is SLC16A1.
- Vesicular secretion goes back to the HMR stoichiometries (2 ATP per 3
  amines or acetylcholine, 1 ATP per 3 GABA, glycine or beta-alanine): VMAT
  exchanges 2 H+ per amine and the V-ATPase pumps about 10 H+ per 3 ATP
  (PMID:32165585), so 1 ATP per molecule overestimates the cost.
- BGT1 (SLC6A12) couples 3 Na+ and 2 Cl- (human BGT-1, PMID:7589472).
- MAR01857/MAR01861 exported bile salts straight from the peroxisome with
  2 ATP and no gene; ABCB11 uses 1 ATP (RHEA:50052/50064), as in
  MAR01870/MAR01872.
- Kept reactions record the reactions #1040 and #1041 deprecated into them.
- #1090 GPR unions: MAR01762 and MAR01605 are CYP27A1 only, MAR01765 is
  CYP7B1 (CH25H only 25-hydroxylates, CYP2E1 and CYP27A1 do not
  7alpha-hydroxylate); duplicate genes removed from joined GPRs.
- MAR08607 has the cross-references and EC of glutamine transaminase K
  (RHEA:30391, EC 2.6.1.117); HCl keeps MNXM735978; MAM03543 has the
  (2E,4Z) structure its name and DECR1 require; renamed ids are recorded in
  metRetired; MAR00751 is merged into MAR06346 with its gene; MAR00036 uses
  one 15-deoxy-PGD2.
- MAR00483 and MAR03838 carry the ids of their quinone reactions, and no
  deprecated reaction lists them as retired any more.
- MAR08433/MAR08434 carry the BiGG/MetaNetX ids of their glycine cleavage
  steps.
- LDHD, a mitochondrial FAD (R)-2-hydroxyacid dehydrogenase (UniProt Q86WU2),
  is removed from the NADH reactions MAR11476/MAR11479.

* fix: corrections to #1101, #1103, #1084, #1086 and #1087 from the curation review

- H2O2 crosses the plasma, ER and inner mitochondrial membranes through
  peroxiporins, as water does through aquaporins in the model: MAR08408
  AQP3/AQP8/AQP9, MAR13002 AQP11/AQP8, MAR03946 AQP8 (PMID:20724658,
  PMID:31546170, PMID:22910329); no longer flagged spontaneous. AQP11 is
  added as a gene.
- Histone import (MAR08024) is by importin-4 (PMID:36103578): GPR IPO4 (new
  gene), cytosol to nucleus only, not spontaneous.
- Prostacyclin and thromboxane A2 hydrolysis are one-way (PMID:29320,
  PMID:1059088); MAR06877 is written as the spontaneous addition of cysteine
  to dopaquinone.
- Leu-Gly-type errors of #1084: palmitoleoylcarnitine and selenohomocysteine
  get their own structures back.
- ModelSEED ids of a different reaction (proton-pumping variants on plain
  hydrolases, 18:2 on 18:3 elongation, degenerate or plant reactions) or
  compound are removed or corrected.
- #1087: unrelated ids on the NH4+/NH3 and xanthurenate protonation
  reactions removed; the respiratory chain BiGG ids NADH2_u10mi, CYOR_u10mi,
  CYOOm2i and CYOOm3i restored; the carnitine-shuttle MetaNetX ids it added
  use (R)-carnitine; ids inherited from misannotated metabolites removed;
  D-ribulose 1-phosphate ids removed from the D-xylulose 1-phosphate
  aldolase; RHEA:10160 restored on MAR09470.
- PubChem ids that #1087 took from wrong model structures (a different
  compound, D-peptides, all-trans lipids) are replaced by the correct one
  where verified, otherwise removed; 14,15-DiHETE has CHEBI:133916 and
  13(S)-HODE loses the 13R LIPID MAPS id.

* fix: remaining corrections from the curation review (deprecated rows, #1099 ids, LDH GPRs, notes)

- Deprecated rows of MAR00449, MAR08610 and MAM01690m are complete copies of
  their last model rows.
- MAR08441 (choline to ubiquinone) loses the ids of the FAD reaction and cites
  the evidence for the quinone route (PMID:1218095, PMID:9630673,
  PMID:34428349).
- #1099: the apo H protein is named as such (CHEBI:29969); the aminomethyl H
  protein has CHEBI:83143 and KEGG C01242 and records MAM02878m as retired;
  S-propanoyldihydrolipoamide loses the protein-bound ids; MAR20168 is named
  after LIPT1 and filed under lipoic acid metabolism.
- TPO iodination (MAR06793/MAR06794) is tyrosine metabolism.
- The 40 PXMP2 pore reactions note that Pxmp2 loss only partly restricts
  peroxisomal permeability (PMID:19352492).
- MAR11476, MAR11479 and MAR05351 get every LDH with demonstrated activity
  (LDHA, LDHB, LDHC, LDHAL6A): LDH reduces the branched-chain 2-oxo acid
  (PMID:26014429) and 2-oxobutyrate (PMID:26059096); LDHAL6B has no
  demonstrated activity.

* fix: remaining low-priority corrections from the curation review

Diffusion and non-enzymatic reactions (#1101, #1103):
- 14 missed diffusion transports (CO, ethanol, methanol, acetaldehyde,
  formaldehyde) flagged spontaneous; acetone diffuses mitochondria <=>
  cytosol <=> extracellular (MAR02222, MAR08364) and is not an MCT1
  substrate (MAR08365 deprecated).
- Plasma-membrane NH3 transport also by RHBG and RHCG (PMID:24077989); the
  NH4+ Rhea id is removed from NH3 transports.
- Lipid nuclear transports named for what they move, with a note on lateral
  diffusion in the ER/nuclear envelope; flagged nuclear diffusions reversible.
- MAM01739 is 5,6-dihydroxyindole (its ChEBI, KEGG and structure); the
  reactions that used it as aminochrome now use aminochrome (MAM02160); the
  two resulting duplicates are merged into the NQO1 and GSTM2 reactions, and
  the two glutathionyl-aminochrome ids into MAM01136.
- MAR04840 (thiosulfate hydrolysis) has no chemical or enzymatic basis and is
  deprecated; H2S2O3 is merged into thiosulfate; mitochondrial TST transfers
  sulfur from GSSH to sulfite (MAR20203, RHEA:55976, PMID:25225291).
- Further non-enzymatic reactions flagged (statin lactonisation, 1-pyrroline,
  aminochrome tautomerisation, mevalonolactone, PAH diols); Pictet-Spengler
  condensations are written as condensations.

Annotations (#1084, #1086, #1087, #1090, #1033):
- Deprecated MetaNetX reaction ids replaced by their matching successor or
  removed; R_-prefixed BiGG ids removed; KEGG ids of the lipoamide
  half-reactions and chloro-compound hydrolyses restored; ModelSEED ids that
  differ in proton coupling removed.
- L-form PubChem/ChEBI ids for stereo-less amino-acid derivatives; iodide,
  ammonium and ascorbate ids of the ionic forms.
- Chemically sensible protonation sites in 11 re-protonated SMILES;
  synaptamide structure; more precise names for kept ids of #1090 merges;
  (11Z)- and (7Z)-octadecenoylcarnitine and cis-vaccenoyl-CoA structures.
- Gene rows of the Ca2+ channels, IPO4 and AQP11 completed; TRPV5 and TRPV6
  added to MAR11311 (UniProt RHEA:29671, PMIDs 11097838, 19261737).
- Genes without evidence removed from #1090 unions: CYP2J2 (MAR06700),
  ALDH3A1/ALDH9A1 (MAR01611); MAR04049 keeps CYP1A1 and CYP1B1.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* fix: corrections to #1043, #1088, #1093-#1096 from an evidence review

Retired pointers for merged ids (MAR09553, MAM02768c, MAM20076n, the
cytosolic tocopherol copies); I-antigen MAM03584g merged into MAM02186g
and MAR08317 into MAR00082 with B4GALT1-4; PTGS peroxidase steps
MAR01307/MAR01392/MAR01393 use NADPH and are irreversible; tocopherol
side-chain beta-oxidation gets ACAA2, and MAR06435/MAR06451 DECR1 and
ECI1/ECI2 with separate NADPH and NAD+; CYP3A4 on MAR08034; references
and notes for MAR03859, MAR04280, MAR20195, MAR20021-MAR20029 and the
CYP4F2 13'-carboxylation steps.

* fix: corrections to #1044 and #1089 from an evidence review

Retinoate 4-hydroxylation and 4-oxo GPRs follow single-isoform human
data (CYP2C9, CYP2B6, CYP2C19, CYP3A5, CYP26A1 added where shown), Rhea
ids for the 9-cis steps, lumped MetaNetX ids removed from the split
hydroxylations, ids for 4-hydroxy-9-cis-retinoate; retired pointers for
the galactose merges and UniProt-backed references for GALK1, GALT and
UGP2 on galactose-1-phosphate.

* chore: update model QC results [skip ci]

* feat: add CYP26C1 to the retinoate 4-hydroxylation and 4-oxo reactions

UniProt Q6V0L0 gives human CYP26C1 the all-trans and 9-cis 4-hydroxylations
and 4-oxo steps (RHEA:51984, 75851, 75847, 75855; PMID:14532297); it also
4-hydroxylates 13-cis-retinoate (PMID:29476041).

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
#1106)

* fix: corrections to #1039, #1041, #1040, #1090, #1028, #1099 and #1038 from the curation review

- MAR20193: MCT1 carries succinate with 2 H+ (UniProt P53985, RHEA:29303,
  PMID:32946811); MCT4 has no succinate evidence, so the GPR is SLC16A1.
- Vesicular secretion goes back to the HMR stoichiometries (2 ATP per 3
  amines or acetylcholine, 1 ATP per 3 GABA, glycine or beta-alanine): VMAT
  exchanges 2 H+ per amine and the V-ATPase pumps about 10 H+ per 3 ATP
  (PMID:32165585), so 1 ATP per molecule overestimates the cost.
- BGT1 (SLC6A12) couples 3 Na+ and 2 Cl- (human BGT-1, PMID:7589472).
- MAR01857/MAR01861 exported bile salts straight from the peroxisome with
  2 ATP and no gene; ABCB11 uses 1 ATP (RHEA:50052/50064), as in
  MAR01870/MAR01872.
- Kept reactions record the reactions #1040 and #1041 deprecated into them.
- #1090 GPR unions: MAR01762 and MAR01605 are CYP27A1 only, MAR01765 is
  CYP7B1 (CH25H only 25-hydroxylates, CYP2E1 and CYP27A1 do not
  7alpha-hydroxylate); duplicate genes removed from joined GPRs.
- MAR08607 has the cross-references and EC of glutamine transaminase K
  (RHEA:30391, EC 2.6.1.117); HCl keeps MNXM735978; MAM03543 has the
  (2E,4Z) structure its name and DECR1 require; renamed ids are recorded in
  metRetired; MAR00751 is merged into MAR06346 with its gene; MAR00036 uses
  one 15-deoxy-PGD2.
- MAR00483 and MAR03838 carry the ids of their quinone reactions, and no
  deprecated reaction lists them as retired any more.
- MAR08433/MAR08434 carry the BiGG/MetaNetX ids of their glycine cleavage
  steps.
- LDHD, a mitochondrial FAD (R)-2-hydroxyacid dehydrogenase (UniProt Q86WU2),
  is removed from the NADH reactions MAR11476/MAR11479.

* fix: corrections to #1101, #1103, #1084, #1086 and #1087 from the curation review

- H2O2 crosses the plasma, ER and inner mitochondrial membranes through
  peroxiporins, as water does through aquaporins in the model: MAR08408
  AQP3/AQP8/AQP9, MAR13002 AQP11/AQP8, MAR03946 AQP8 (PMID:20724658,
  PMID:31546170, PMID:22910329); no longer flagged spontaneous. AQP11 is
  added as a gene.
- Histone import (MAR08024) is by importin-4 (PMID:36103578): GPR IPO4 (new
  gene), cytosol to nucleus only, not spontaneous.
- Prostacyclin and thromboxane A2 hydrolysis are one-way (PMID:29320,
  PMID:1059088); MAR06877 is written as the spontaneous addition of cysteine
  to dopaquinone.
- Leu-Gly-type errors of #1084: palmitoleoylcarnitine and selenohomocysteine
  get their own structures back.
- ModelSEED ids of a different reaction (proton-pumping variants on plain
  hydrolases, 18:2 on 18:3 elongation, degenerate or plant reactions) or
  compound are removed or corrected.
- #1087: unrelated ids on the NH4+/NH3 and xanthurenate protonation
  reactions removed; the respiratory chain BiGG ids NADH2_u10mi, CYOR_u10mi,
  CYOOm2i and CYOOm3i restored; the carnitine-shuttle MetaNetX ids it added
  use (R)-carnitine; ids inherited from misannotated metabolites removed;
  D-ribulose 1-phosphate ids removed from the D-xylulose 1-phosphate
  aldolase; RHEA:10160 restored on MAR09470.
- PubChem ids that #1087 took from wrong model structures (a different
  compound, D-peptides, all-trans lipids) are replaced by the correct one
  where verified, otherwise removed; 14,15-DiHETE has CHEBI:133916 and
  13(S)-HODE loses the 13R LIPID MAPS id.

* fix: remaining corrections from the curation review (deprecated rows, #1099 ids, LDH GPRs, notes)

- Deprecated rows of MAR00449, MAR08610 and MAM01690m are complete copies of
  their last model rows.
- MAR08441 (choline to ubiquinone) loses the ids of the FAD reaction and cites
  the evidence for the quinone route (PMID:1218095, PMID:9630673,
  PMID:34428349).
- #1099: the apo H protein is named as such (CHEBI:29969); the aminomethyl H
  protein has CHEBI:83143 and KEGG C01242 and records MAM02878m as retired;
  S-propanoyldihydrolipoamide loses the protein-bound ids; MAR20168 is named
  after LIPT1 and filed under lipoic acid metabolism.
- TPO iodination (MAR06793/MAR06794) is tyrosine metabolism.
- The 40 PXMP2 pore reactions note that Pxmp2 loss only partly restricts
  peroxisomal permeability (PMID:19352492).
- MAR11476, MAR11479 and MAR05351 get every LDH with demonstrated activity
  (LDHA, LDHB, LDHC, LDHAL6A): LDH reduces the branched-chain 2-oxo acid
  (PMID:26014429) and 2-oxobutyrate (PMID:26059096); LDHAL6B has no
  demonstrated activity.

* fix: remaining low-priority corrections from the curation review

Diffusion and non-enzymatic reactions (#1101, #1103):
- 14 missed diffusion transports (CO, ethanol, methanol, acetaldehyde,
  formaldehyde) flagged spontaneous; acetone diffuses mitochondria <=>
  cytosol <=> extracellular (MAR02222, MAR08364) and is not an MCT1
  substrate (MAR08365 deprecated).
- Plasma-membrane NH3 transport also by RHBG and RHCG (PMID:24077989); the
  NH4+ Rhea id is removed from NH3 transports.
- Lipid nuclear transports named for what they move, with a note on lateral
  diffusion in the ER/nuclear envelope; flagged nuclear diffusions reversible.
- MAM01739 is 5,6-dihydroxyindole (its ChEBI, KEGG and structure); the
  reactions that used it as aminochrome now use aminochrome (MAM02160); the
  two resulting duplicates are merged into the NQO1 and GSTM2 reactions, and
  the two glutathionyl-aminochrome ids into MAM01136.
- MAR04840 (thiosulfate hydrolysis) has no chemical or enzymatic basis and is
  deprecated; H2S2O3 is merged into thiosulfate; mitochondrial TST transfers
  sulfur from GSSH to sulfite (MAR20203, RHEA:55976, PMID:25225291).
- Further non-enzymatic reactions flagged (statin lactonisation, 1-pyrroline,
  aminochrome tautomerisation, mevalonolactone, PAH diols); Pictet-Spengler
  condensations are written as condensations.

Annotations (#1084, #1086, #1087, #1090, #1033):
- Deprecated MetaNetX reaction ids replaced by their matching successor or
  removed; R_-prefixed BiGG ids removed; KEGG ids of the lipoamide
  half-reactions and chloro-compound hydrolyses restored; ModelSEED ids that
  differ in proton coupling removed.
- L-form PubChem/ChEBI ids for stereo-less amino-acid derivatives; iodide,
  ammonium and ascorbate ids of the ionic forms.
- Chemically sensible protonation sites in 11 re-protonated SMILES;
  synaptamide structure; more precise names for kept ids of #1090 merges;
  (11Z)- and (7Z)-octadecenoylcarnitine and cis-vaccenoyl-CoA structures.
- Gene rows of the Ca2+ channels, IPO4 and AQP11 completed; TRPV5 and TRPV6
  added to MAR11311 (UniProt RHEA:29671, PMIDs 11097838, 19261737).
- Genes without evidence removed from #1090 unions: CYP2J2 (MAR06700),
  ALDH3A1/ALDH9A1 (MAR01611); MAR04049 keeps CYP1A1 and CYP1B1.

* chore: update model QC results [skip ci]

* fix: pre-existing reaction and GPR errors found in the curation review

- MAR01453 hydroxypyruvate reductase is 1:1 (it made reducing equivalents),
  with LDHA/LDHB (PMID:16198644).
- No cytosolic NAD+ choline dehydrogenase or NAD+ D-lactate dehydrogenase
  exists in human: MAR04696 (with the cytosolic betaine aldehyde reactions
  MAR04697/MAR04698) and MAR08512 are deprecated; LDHD is removed from NAD
  reactions; choline is oxidised by CHDH in mitochondria.
- CH25H only 25-hydroxylates cholesterol: the ER 26- and 20-hydroxylations
  MAR01785/MAR01781 (and MAR01783, which followed from 20-hydroxycholesterol)
  are deprecated.
- NIS carries 2 Na+ per iodide (MAR06989 into MAR08924); pendrin exchanges
  1 Cl- per I- (RHEA:72379); the H+/K+-ATPase exports its proton
  (MAR08892; MAR01444 merged into it).
- HCl is replaced by chloride + H+; hydrogen bromide was bromide already and
  is renamed and re-annotated.
- 15-deoxy-PGD2 forms by dehydration of PGD2 (one-way, spontaneous); the
  1-pyrroline-5-carboxylate ring openings are non-enzymatic (no ALDH4A1);
  nuclear transports of CMP-Neu5Ac, UTP and deoxycytidine are diffusion,
  like the other nucleotides (NUP62CL and NUP210L, left on no reaction, are
  removed); MAR01597 and MAR10395 are deprecated.
- CYP27A1 steps are mitochondrial monooxygenations; 15-oxo-PGE2 13-reduction
  by PTGR3 (MAR20018) is written in its direction.
- 2-hydroxyglutarate: L- (MAM03188) and D-2-hydroxyglutarate (MAM20012) are
  separate; L2HGDH oxidises L-2HG in mitochondria (ubiquinone), new D2HGDH
  (MAR20204, via ETF) oxidises D-2HG; PHGDH makes D-2HG; MDH2 and
  LDHA/MDH1 make L-2HG; OAT1/NaDC3 transports.
- MAR08773 is named for D-xylulose 1-phosphate.

* fix: bile acid, globoside, 2-hydroxyglutarate export and PGD2 dehydration reactions

- No human enzyme makes beta-GalNAc-globoside (para-Forssman): B3GALNT1 only
  makes globoside and B3GALNT2 does not act on glycolipids (PMID:14724282);
  its synthesis, reversed lysosomal breakdown and transports are deprecated.
  NAGA hydrolyses alpha-GalNAc-globoside with water (RHEA:48412).
- MAR01787, MAR03259, MAR02184 and MAR02881 turned sterols without a
  7alpha-hydroxyl into 7alpha-hydroxycholest-4-en-3-one; HSD3B7 acts only on
  7alpha-hydroxylated sterols (PMID:11067870); deprecated.
- CYP27A1 is mitochondrial (UniProt Q02318): duplicate and cytosolic copies
  of its steps are merged into the mitochondrial reactions.
- 2-hydroxyglutarate efflux (MAR11733) is OAT4 exchange with estrone sulfate
  (PMID:18365245).
- PGD2 dehydrates to delta-12-PGJ2 and PGJ2 non-enzymatically (one-way;
  PMID:6578214, PMID:11786541).

* fix: review corrections on the reaction branch

- Deprecations left dead ends: the Golgi GalNAc transport MAR08193, and
  MAR01578/MAR02895 (20-hydroxycholesterol and 7alpha-hydroxycholest-4-en-3-one
  in mitochondria, each in one reaction only) are deprecated with their
  metabolites.
- OAT4 also exchanges D-2-hydroxyglutarate (MAR20211, PMID:18365245).
- The L-glycerate ids MNXR100701/HPYRR2x belong to the LDH hydroxypyruvate
  reduction (MAR01453), not to GRHPR's D-glycerate reaction (MAR08782).
- Pendrin 1:1 exchange cited (PMID:18565999); an unrelated citation removed
  from MAR11731; MAR02184 is deprecated without a merge target; chloride
  records the HMR id of the merged HCl.

* chore: update model QC results [skip ci]

* fix: MAR01453 lists its BiGG id once

* chore: update model QC results [skip ci]

* fix: deprecate the blocked ER copy MAR20031 of 4-nitrophenol hydroxylation into MAR08034

MAR20031 is MAR08034 (CYP2E1, RHEA:26205, MNXR102281) in the ER, where PNP
and 4-nitrocatechol have no other reaction or transport, so it cannot carry
flux. MAR08034 keeps the connected cytosolic copy and records MAR20031 in
rxnRetired; PNP[r] and 4-nitrocatechol[r] are deprecated.

* chore: update model QC results [skip ci]

* fix: cholesterol synthesis goes through HSD17B7 and MSMO1 in the ER copy

The Recon3D ER copy of the second C4 demethylation lumped it into
3-keto-4-methylzymosterol + O2 -> zymosterone (MAR00117, MAR00118) and
had a gene-less hydrolytic demethylation (MAR02302), which bypassed
HSD17B7 and MSMO1; these are deprecated and replaced by MSMO1 (MAR20217)
and NSDHL (MAR20218) steps with the new MAM00955r. MAR00115 gets HSD17B7
instead of HSD17B4, MAR01500 and MAR02301 reduce with NADPH instead of
producing it, and MAR00116 gets EC 1.14.18.9.

* chore: update model QC results [skip ci]

* fix: do not complete the ER copy of cholesterol synthesis

The MSMO1 and NSDHL steps in the ER (and 4alpha-carboxyzymosterol[r])
are dropped: the cytosolic and ER copies of the pathway are to be
reconciled into one, and cholesterol is made by the complete cytosolic
copy.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…errors (#1107)

* fix: corrections to #1039, #1041, #1040, #1090, #1028, #1099 and #1038 from the curation review

- MAR20193: MCT1 carries succinate with 2 H+ (UniProt P53985, RHEA:29303,
  PMID:32946811); MCT4 has no succinate evidence, so the GPR is SLC16A1.
- Vesicular secretion goes back to the HMR stoichiometries (2 ATP per 3
  amines or acetylcholine, 1 ATP per 3 GABA, glycine or beta-alanine): VMAT
  exchanges 2 H+ per amine and the V-ATPase pumps about 10 H+ per 3 ATP
  (PMID:32165585), so 1 ATP per molecule overestimates the cost.
- BGT1 (SLC6A12) couples 3 Na+ and 2 Cl- (human BGT-1, PMID:7589472).
- MAR01857/MAR01861 exported bile salts straight from the peroxisome with
  2 ATP and no gene; ABCB11 uses 1 ATP (RHEA:50052/50064), as in
  MAR01870/MAR01872.
- Kept reactions record the reactions #1040 and #1041 deprecated into them.
- #1090 GPR unions: MAR01762 and MAR01605 are CYP27A1 only, MAR01765 is
  CYP7B1 (CH25H only 25-hydroxylates, CYP2E1 and CYP27A1 do not
  7alpha-hydroxylate); duplicate genes removed from joined GPRs.
- MAR08607 has the cross-references and EC of glutamine transaminase K
  (RHEA:30391, EC 2.6.1.117); HCl keeps MNXM735978; MAM03543 has the
  (2E,4Z) structure its name and DECR1 require; renamed ids are recorded in
  metRetired; MAR00751 is merged into MAR06346 with its gene; MAR00036 uses
  one 15-deoxy-PGD2.
- MAR00483 and MAR03838 carry the ids of their quinone reactions, and no
  deprecated reaction lists them as retired any more.
- MAR08433/MAR08434 carry the BiGG/MetaNetX ids of their glycine cleavage
  steps.
- LDHD, a mitochondrial FAD (R)-2-hydroxyacid dehydrogenase (UniProt Q86WU2),
  is removed from the NADH reactions MAR11476/MAR11479.

* fix: corrections to #1101, #1103, #1084, #1086 and #1087 from the curation review

- H2O2 crosses the plasma, ER and inner mitochondrial membranes through
  peroxiporins, as water does through aquaporins in the model: MAR08408
  AQP3/AQP8/AQP9, MAR13002 AQP11/AQP8, MAR03946 AQP8 (PMID:20724658,
  PMID:31546170, PMID:22910329); no longer flagged spontaneous. AQP11 is
  added as a gene.
- Histone import (MAR08024) is by importin-4 (PMID:36103578): GPR IPO4 (new
  gene), cytosol to nucleus only, not spontaneous.
- Prostacyclin and thromboxane A2 hydrolysis are one-way (PMID:29320,
  PMID:1059088); MAR06877 is written as the spontaneous addition of cysteine
  to dopaquinone.
- Leu-Gly-type errors of #1084: palmitoleoylcarnitine and selenohomocysteine
  get their own structures back.
- ModelSEED ids of a different reaction (proton-pumping variants on plain
  hydrolases, 18:2 on 18:3 elongation, degenerate or plant reactions) or
  compound are removed or corrected.
- #1087: unrelated ids on the NH4+/NH3 and xanthurenate protonation
  reactions removed; the respiratory chain BiGG ids NADH2_u10mi, CYOR_u10mi,
  CYOOm2i and CYOOm3i restored; the carnitine-shuttle MetaNetX ids it added
  use (R)-carnitine; ids inherited from misannotated metabolites removed;
  D-ribulose 1-phosphate ids removed from the D-xylulose 1-phosphate
  aldolase; RHEA:10160 restored on MAR09470.
- PubChem ids that #1087 took from wrong model structures (a different
  compound, D-peptides, all-trans lipids) are replaced by the correct one
  where verified, otherwise removed; 14,15-DiHETE has CHEBI:133916 and
  13(S)-HODE loses the 13R LIPID MAPS id.

* fix: remaining corrections from the curation review (deprecated rows, #1099 ids, LDH GPRs, notes)

- Deprecated rows of MAR00449, MAR08610 and MAM01690m are complete copies of
  their last model rows.
- MAR08441 (choline to ubiquinone) loses the ids of the FAD reaction and cites
  the evidence for the quinone route (PMID:1218095, PMID:9630673,
  PMID:34428349).
- #1099: the apo H protein is named as such (CHEBI:29969); the aminomethyl H
  protein has CHEBI:83143 and KEGG C01242 and records MAM02878m as retired;
  S-propanoyldihydrolipoamide loses the protein-bound ids; MAR20168 is named
  after LIPT1 and filed under lipoic acid metabolism.
- TPO iodination (MAR06793/MAR06794) is tyrosine metabolism.
- The 40 PXMP2 pore reactions note that Pxmp2 loss only partly restricts
  peroxisomal permeability (PMID:19352492).
- MAR11476, MAR11479 and MAR05351 get every LDH with demonstrated activity
  (LDHA, LDHB, LDHC, LDHAL6A): LDH reduces the branched-chain 2-oxo acid
  (PMID:26014429) and 2-oxobutyrate (PMID:26059096); LDHAL6B has no
  demonstrated activity.

* fix: remaining low-priority corrections from the curation review

Diffusion and non-enzymatic reactions (#1101, #1103):
- 14 missed diffusion transports (CO, ethanol, methanol, acetaldehyde,
  formaldehyde) flagged spontaneous; acetone diffuses mitochondria <=>
  cytosol <=> extracellular (MAR02222, MAR08364) and is not an MCT1
  substrate (MAR08365 deprecated).
- Plasma-membrane NH3 transport also by RHBG and RHCG (PMID:24077989); the
  NH4+ Rhea id is removed from NH3 transports.
- Lipid nuclear transports named for what they move, with a note on lateral
  diffusion in the ER/nuclear envelope; flagged nuclear diffusions reversible.
- MAM01739 is 5,6-dihydroxyindole (its ChEBI, KEGG and structure); the
  reactions that used it as aminochrome now use aminochrome (MAM02160); the
  two resulting duplicates are merged into the NQO1 and GSTM2 reactions, and
  the two glutathionyl-aminochrome ids into MAM01136.
- MAR04840 (thiosulfate hydrolysis) has no chemical or enzymatic basis and is
  deprecated; H2S2O3 is merged into thiosulfate; mitochondrial TST transfers
  sulfur from GSSH to sulfite (MAR20203, RHEA:55976, PMID:25225291).
- Further non-enzymatic reactions flagged (statin lactonisation, 1-pyrroline,
  aminochrome tautomerisation, mevalonolactone, PAH diols); Pictet-Spengler
  condensations are written as condensations.

Annotations (#1084, #1086, #1087, #1090, #1033):
- Deprecated MetaNetX reaction ids replaced by their matching successor or
  removed; R_-prefixed BiGG ids removed; KEGG ids of the lipoamide
  half-reactions and chloro-compound hydrolyses restored; ModelSEED ids that
  differ in proton coupling removed.
- L-form PubChem/ChEBI ids for stereo-less amino-acid derivatives; iodide,
  ammonium and ascorbate ids of the ionic forms.
- Chemically sensible protonation sites in 11 re-protonated SMILES;
  synaptamide structure; more precise names for kept ids of #1090 merges;
  (11Z)- and (7Z)-octadecenoylcarnitine and cis-vaccenoyl-CoA structures.
- Gene rows of the Ca2+ channels, IPO4 and AQP11 completed; TRPV5 and TRPV6
  added to MAR11311 (UniProt RHEA:29671, PMIDs 11097838, 19261737).
- Genes without evidence removed from #1090 unions: CYP2J2 (MAR06700),
  ALDH3A1/ALDH9A1 (MAR01611); MAR04049 keeps CYP1A1 and CYP1B1.

* chore: update model QC results [skip ci]

* fix: pre-existing metabolite identity, structure and cross-reference errors

- L-carnitine (MAM02348) had the D-carnitine structure and ids in every field
  but HMDB: now (R)-carnitine (CHEBI:16347, C00318, MNXM1105737, cpd00266,
  PubChem 10917); the carnitine-shuttle reactions carry the (R)-carnitine
  MetaNetX and ModelSEED ids.
- Peptides: 221 peptide metabolites had D or undefined residues; they get
  all-L structures, and all-L PubChem/MetaNetX ids replace the D ones.
- Statins: the rosuvastatin, pitavastatin, pravastatin, fluvastatin and
  cerivastatin metabolites were Ca/Na salt dimers (cerivastatin also one CH2
  short); they are monomers at physiological charge, and their reactions are
  rewritten to stay balanced; statin acyl glucuronide lactonisation by
  UGT1A1/UGT1A3/UGT2B7 (PMID:26412035).
- D-xylulose 1-phosphate, beta-GalNAc-globoside (penicillin G ids removed),
  4-cis-decenoyl-CoA, (6Z,9Z)-octadecadienoyl-CoA, 10,11-dihydro-12-epi-LTB4,
  A2PE, lactosylceramide and alpha-/omega-muricholoyl-CoA get their own
  structures and cross-references; the reaction ids that followed the wrong
  compound are corrected or removed; KHK (MAR08772) has the D-xylulose
  MetaNetX id.
- Shifted BiGG ids from the HMR/Recon mapping: 59 exchanges carry their own
  EX_ id, and 97 internal reactions the matching BiGG and MetaNetX ids.

* fix: merge duplicate lactosylceramide and Leb glycolipid ids; linoleoyl-CoA, dihydro-LTB4 and peptide names

- MAM03586 is merged into MAM02328 (lactosylceramide), which now carries
  lactosylceramide's BiGG/MetaNetX ids instead of digalactosylceramide's; the
  six Recon reactions that became identical to HMR ones are merged into them,
  and the GLA/PSAP globotriaosylceramide hydrolysis (MAR00804) gets its genes.
- MAM01999 'glycolipid' is the Lewis b glycolipid (two fucoses, made by FUT3)
  and is merged into MAM03578; MAR08287 makes Lewis y (FUT9); duplicate
  reactions merged; MAR08271 is FUT3 or FUT5.
- Linoleoyl-CoA has the (9Z,12Z) PubChem id; 10,11-dihydro-LTB4 and its CoA
  ester the 5S,12S structure and ids (CHEBI:133324); MAM00091m's metRetired
  is well formed.
- Asp-Ala-Arg, Glu-Arg-Leu and Glu-Asn-Leu are named by their formula, BiGG
  ids and hydrolysis products.

* fix: review corrections on the metabolite branch

- MAR02660/MAR02662 unchanged (acyl-CoA reactions follow their own issues).
- Cerivastatin M-22 and M-31 are the neutral lactones of M-1 and M-23, and
  the M-1/M-23 glucuronides are acyl glucuronides (only these lactonise); the
  lactonisation and hydrolysis reactions no longer use SAM/SAH to balance.
- The fluvastatin acids have the 3R,5S configuration of fluvastatin and its
  lactone (PubChem 37888681).
- MetaNetX ids of other isomers removed (MAR00150; the 10,11-dihydro-LTB4
  reactions; two polyglutamyl-folates); MAR00766 duplicates MAR12035 in
  reverse and is merged into it; exchange names follow the renamed peptides;
  FUT5 evidence cited on MAR08271.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* ci: relative links in the committed test-result reports

model_qc_summary.md and gene-essential_summary.md sit in data/testResults
next to the README and the CSVs they link to, so they now link relatively.
The absolute links pinned the pull-request branch and broke once it was
deleted after merging. The posted comments keep absolute links, which they
need.

* chore: relative links in the committed test-result reports

* chore: add gene essentiality test result [skip ci]
…release (#1116)

* fix: separate the Ca2+ ATPases by membrane and add ER and Golgi Ca2+ release (#1000)

MAR07629 exported 2 Ca2+ per ATP with the GPR of all Ca2+ ATPase
families; it is deprecated into the PMCA reaction MAR00119, which gets
its ATP-hydrolysis proton in the cytosol and EC 7.2.2.10. SERCA pumps
Ca2+ into the ER and Golgi, SPCA into the Golgi, and IP3 receptors,
ryanodine receptors and TMCO1 release it again (new Ca2+[r], Ca2+[g],
MAR20212-MAR20216 and seven genes).

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* ci: halve the ftINIT time limit in the gene-essentiality test

* fix: keep develop's reinstated vesicle-transport reactions out of the deprecated list

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
) (#1121)

* fix: vitamin D C-23 pathway to CYP24A1, FAD hydrolysis to NT5E, drop SDR16C5 from 9-cis-retinol reactions (#887)

* chore: update model QC results [skip ci]

* fix: take the vitamin D C-23 and 24-oxo biomass catabolites from mitochondria and drop the cytosolic CYP24A1 pathway copies (#887)

* chore: update model QC results [skip ci]

* chore: add gene essentiality test result [skip ci]

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…unit tests (#1078)

* feat: automate the release from develop to a published GitHub release

Releases were cut by running code/io/increaseHumanGEMVersion.py by hand on
main, which left the split between main and develop unenforced: v2.0.1
dropped the generated model files and needed a follow-up commit to restore
them.

- `release.yml`: cuts `release/<version>` from `develop`, stamps the version
  and regenerates the exports, opens the release pull request into `main`,
  and on merge tags `v<version>`, publishes the release from its notes file
  and opens the sync-back pull request into `develop`.
- `branch-hygiene.yml`: fails when a generated model file or `version.txt` is
  tracked on `develop`. Exact paths, since a `*.xlsx` or `*.txt` glob would
  match the Hart 2015 datasets and the requirements files.
- `increaseHumanGEMVersion.py`: takes an explicit version through `validate`
  and `build` subcommands instead of bumping from the branch it runs on, so
  the build runs on a release branch cut from `develop`. `validate` reads the
  released version from `origin/main` and refuses a version whose release
  notes are missing.
- `docs/releaseNotes/<version>.md`: the body of the GitHub release, written
  before the release branch is cut and reviewed with the release pull request.

* chore: update model QC results [skip ci]

* test: add an SBML round-trip gate, a naming check, and unit tests for the QC code

The scripts under code/test decide whether a pull request passes model QC, and
model/Human-GEM.xml is written at release time and read back by nothing, so both
were unverified.

- `testSbmlConversion.py`: writes the annotated model to SBML, reads it back and
  compares the two, which catches annotation losses and identifier rewrites in the
  file that is published as SBML. Identifiers are compared as sets, because the
  writer emits an identifier listed twice on one entity only once, and returns a
  single cross-reference as a string rather than a one-element list. Wired into
  `model-qc.yml` as `roundtrip_sbml`, a gate alongside the YAML round-trips.
- `qcModelChecks.check_name_consistency`: reports entities with no name and
  metabolites whose name differs between compartments. `reactions.tsv` and
  `metabolites.tsv` carry cross-references only, so a name has no second copy to
  drift against, but one chemical can still be named differently per compartment.
- `code/test/unit`: pytest cover for `qcStatus`, `buildReport`'s counting and delta
  helpers, and the new naming check, run with ruff by `python-tests.yml` on every
  pull request. They build small cobra models, so they need no solver or licence.
- `qcStatus.read_status`: the default path was bound at import, so a reassigned
  STATUS_FILE was written but not read. It is resolved per call now.

* test: pin the ruff rule set instead of relying on its defaults

An unpinned ruff in CI applies whatever rule set its current release defaults to,
so a ruff release fails the build rather than a change to this repository. The
selected rules report mistakes rather than layout; line length is left out because
nothing formats this repository and the existing scripts run to 147 characters.

* test: declare the PyYAML dependency of qcModelChecks

qcModelChecks.py reads model/Human-GEM.yml with PyYAML to find duplicate keys
before cobra loads the file, but reached it through another package's
dependencies rather than declaring it, so a job installing only these
requirements failed on the import.

* chore: update model QC results [skip ci]

* fix: stash untracked result files before the QC branch checkout

A result file produced for the first time is untracked in the run that writes it,
but is tracked at the branch tip as soon as an earlier run has committed it. Plain
git stash leaves untracked files in the working tree, so the checkout that follows
refused to overwrite them and the job failed after every check had already run.

* chore: update model QC results [skip ci]

* chore: full MEMOTE result [skip ci]

* docs: add the release notes of every earlier release

* chore: update model QC results [skip ci]

* feat: write the release notes in the release pull request, not in a file

The notes are published as the text of the GitHub release, which Zenodo
archives with each version, so a copy in the repository only duplicates it.
The release pull request opens with a notes placeholder, a check keeps it
from merging until the notes are written, and the merged description is
published as the release text.

* feat: publish the release pull request's description as the release notes

The release pull request opens with a notes placeholder, a check keeps it
from merging until the notes are written, and the merged description is
published as the release text. validate only checks the version increment.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Model quality report

⚠️ 3 pre-existing finding(s), no regressions vs main. Non-blocking. Gates: duplicate keys 0 ✅, growth 1.9 ✅.

Section Status
Model & network checks (20) ⚠️ 3 pre-existing
Model file & metabolic tasks (6) ✅ all pass
MEMOTE ✨ 64.9% (full suite)
Full report model_qc_summary.md

Improved:

  • Cross-refs inconsistent across compartments: 0
  • Mass-imbalanced reactions: 69
  • Charge-imbalanced reactions: 172
  • Structure vs formula/charge inconsistencies: 57

Pre-existing:

  • Reactions split across compartments: 26
  • Reactions flagged by MACAW dead-end test: 1111
  • Reactions flagged as MACAW duplicates: 340

✅ unchanged · ✨ improved vs main · ⚠️ pre-existing, non-blocking · ❌ regression

Full workflow run · this comment is edited as results come in

…n the checks (#1124)

* ci: accept the RAVEN 3 YAML format in the checks and the release

- YAML lint: allow list items at their key's indentation, as writeYAMLmodel.m
  (RAVEN 3) and raven-toolbox write them.
- structureConsistencyTest: read charges written as floats (-1.0).
- increaseHumanGEMVersion build: stamp the release date, which the writer
  otherwise keeps from the stored model.
- release: keep develop's Human-GEM.yml in the sync-back, as the build stamps
  the version into the release copy.

* chore: write Human-GEM.yml in the RAVEN 3 YAML format

Rewritten by raven-toolbox's write_yaml_model, as the release build writes it
(and as v2.0.1 was released), so develop and main use one format. No model
content changes: reading the file before and after gives the same metabolites,
reactions, genes and compartments; only a reaction's subsystem is now a
one-element list and the metaData bounds are numbers.

qcModelChecks' split-compartment check accepts a subsystem written as a list.

* chore: update model QC results [skip ci]

* chore: update model QC results [skip ci]

* ci: run the checks with raven-toolbox#164

* chore: update model QC results [skip ci]
The package now follows the model's version.
main was re-created as a new root commit on 2026-09-09 (v2.0.1 plus the release
workflow) and shares no history with develop. This merge keeps the release
branch's content unchanged and makes main an ancestor, so the release pull
request merges into main and the sync-back merges main into develop.
@edkerk

edkerk commented Sep 30, 2026

Copy link
Copy Markdown
Member

/run gene-essentiality

@edkerk

edkerk commented Sep 30, 2026

Copy link
Copy Markdown
Member

/run memote

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

Gene essentiality was run on this pull request via GH Actions.

Gene essentiality: effect of this change

Checked 319 gene(s) directly affected plus 2368 more that share a metabolite with one of them.

A gene counts as changed below only if the flip agrees in direction across at least 3 of the 5 cell-line models; fewer than that is grouped as likely noise from ftINIT's run-to-run variability instead.

Category Status
Growth effect changed (vs Hart 2015) ✅ 0
Other role changed (not Hart-comparable) ✅ 0
Likely noise (<3/5 lines) ✅ 0
No change ✅ 2682

Full detail: gene-essential_summary.md.

✅ unchanged · ✨ correct vs Hart 2015 · ❌ wrong vs Hart 2015 · ℹ️ not comparable to Hart 2015

The unscoped, every-gene matrix behind the table above is committed to gene-essential.csv.

Note: each run posts its results in place of its own start notice; earlier results link here.

@github-actions

Copy link
Copy Markdown
Contributor Author

🔬 Started the full MEMOTE suite on release/2.1.0 (this takes hours). The score will update the Model QC comment when it finishes.

@edkerk
edkerk merged commit d1364df into main Oct 5, 2026
@edkerk
edkerk deleted the release/2.1.0 branch October 5, 2026 14:03
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.

2 participants