Skip to content

Testing for conflicts - #84

Open
cshenry wants to merge 444 commits into
ModelSEED:dev_updatefrom
cshenry:main
Open

cshenry wants to merge 444 commits into
ModelSEED:dev_updatefrom
cshenry:main

Conversation

@cshenry

@cshenry cshenry commented Oct 6, 2022

Copy link
Copy Markdown
Contributor

No description provided.

Fxe and others added 30 commits July 18, 2023 01:48
Christopher Henry and others added 30 commits November 9, 2025 23:17
…fixes to community and flexible biomass and thermo and adding features to msexpression
MSTemplateBuilder.from_dict() was silently dropping the drain_list
from template JSON files, so template.drains stayed empty. This
meant MSBuilder.build_drains() fell back to DEFAULT_SINKS and only
created SK_ (sink) reactions, never DM_ (demand) reactions.

Without DM_cpd11416_c0, cobrapy FBA returns 0 growth because the
biomass compound (cpd11416) produced by bio1 has no outlet.

Two fixes:
1. mstemplate.py: from_dict() now reads drain_list into builder.drains
2. msbuilder.py: build_demands() creates DM_ reactions for all drain
   compounds (previously required lower_bound < 0, but template
   drain_list entries use [0, 1000])

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix drain_list loading: create DM_ demand reactions during model build
…dd frontmatter

- Replace 306-line obsolete CLAUDE.md with focused tier-split version
- Remove non-home expert/dev skills (only universal + home-repo skills remain)
- Retire: run_headless, free-agent, analyze-email, generate-tasks, create-prd, create-skill
- Add YAML frontmatter (name, description, scope) to all surviving skills

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convention pivot: .claude/commands/ is now runtime-only (gitignored,
populated by `claude-skills sync`). Source-of-truth lives in
agent-io/skills/.
The docstrings for MSGapfill.run_multi_gapfill and
MSGapfill.integrate_gapfill_solution listed "Cumulative" and
"Simultaneous" as valid gapfilling_mode values, but the code only branches
on "Sequential", "Independent", and "Global". Passing one of the historical
names silently fell through all branches and produced a no-op.

Rewrote both docstrings to describe the three real modes accurately and
explicitly call out that "Cumulative" and "Simultaneous" are not honored.
Also updated two inline comments that referred to "cumulative" /
"simultaneous" gapfilling when describing the Sequential / Global branches.

Doc-only change: no logic, signatures, branches, or defaults modified.
The cumulative_solution local variable and cumulative_gapfilling attribute
are preserved as-is (they refer to the solution list, not the mode name).
…feature lookup

Two related changes that unblock the modelseed-api bulk-reconstruction
endpoint (Phase 3 per Chris Henry's PRD).

1. New factory: AnnotationOntology.from_prd_input(genome_id, annotations,
   data_dir, translator, ...). Mirrors from_kbase_data but for inputs
   whose ontology terms have NOT been pre-translated to ModelSEED
   reaction IDs. The translator callable is injected so the factory
   stays decoupled from any specific translation backend
   (KBUtilLib.KBAnnotationUtils.translate_term_to_modelseed is the
   canonical impl; tests use a small in-memory fake).

   Input shape: {gene_id: {ontology_type: [{term, score}, ...]}}.
   Synthesizes one AnnotationOntologyEvent per ontology type, keeps
   priority-list logic untouched. Unmapped terms (translator returns
   []) are retained with an empty msrxns set - per PRD, unmapped genes
   must never silently disappear.

2. Fix latent bug at msbuilder.py:789 in build_from_annotaton_ontology.
   The line called anno_ont.get_feature(gene.id) but AnnotationOntology
   has no such method (features are keyed in genes or cdss dicts). Any
   call path that reached this line would AttributeError as soon as it
   tried to attach evidence to a built reaction. Replaced with the
   correct accessor: anno_ont.genes.get(gene.id) or anno_ont.cdss.get(gene.id).

9 unit tests cover both changes:
- 7 cases for from_prd_input (happy path, multi-gene/multi-ontology,
  score recording, default score, unmapped retention, namespaced-term
  passing, empty input)
- 2 regression tests for the msbuilder fix (one positive, one negative
  lock to flag if get_feature is ever added back without reconciling
  the msbuilder line)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e in run_multi_gapfill

Two-stage gapfilling: Stage 1 maximizes simultaneously-active native reactions
(ReactionActivationPkg + RevBinPkg MILP), Stage 2 minimizes database additions
that sustain those locked active directions while producing biomass.  The method
is exposed both as MSGapfill.run_comprehensive_gapfilling() and as
gapfilling_mode='Comprehensive' in run_multi_gapfill (integrating cumulatively
like Sequential across multiple media).

Six integration tests cover: correct return shape, positive biomass after
integration, comprehensive > sequential active-reaction count, no native
reaction removal, gfmodel reusability after lock release, and correct shape
from run_multi_gapfill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ease

Activation variable lower-bounds are now unconditionally released via a
finally block, even if an unexpected exception fires during Stage 2 or
test-condition evaluation. Functional behaviour is unchanged; all six
integration tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… run

set_base_objective built the biomass>=minobjective constraint directly but
never updated self.parameters["minimum_obj"]. Because test_gapfill_database
later restores the biomass floor via reset_objective_minimum(parameters[
"minimum_obj"]), a run_gapfilling(minimum_obj=X) with X above the construction
-time default was silently relaxed back to the default before the final
optimization -- so the gapfiller returned the cheapest reaction set reaching
the default threshold rather than the requested growth. Keep parameters[
"minimum_obj"] in sync inside set_base_objective. Fixes test_ms_gap_fill2
(growth 0.8 now re-adds the full oxidative-phosphorylation set: GLCpts, O2t,
NADH16, CYTBD).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mode

Comprehensive gapfilling activates the model's native reactions as a
collectively-necessary set; per-reaction knockout pruning classifies each as
individually redundant and strips them, leaving the model unable to grow.
run_multi_gapfill now overrides remove_unneeded_reactions to False (with a
warning) for gapfilling_mode='Comprehensive' so callers cannot defeat the
activation by leaving the default True in place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…V->V in Gibbs constraint

The membrane term in build_gibbs_constraint was computing
  charge * Faraday(kC/mol) * compartment_potential
without the /1000 factor needed to convert the mV input to V.
With the default c0 = -160 mV this inflated the term by ~1000x
(charge * 96.485 * -160 kJ/mol instead of charge * 96.485 * -0.16 kJ/mol),
driving TMFA infeasible for any charged cytoplasmic metabolite.

Changes:
- build_gibbs_constraint: divide compartment_potential by 1000 (mV->V)
  so that Faraday(kC/mol) * V = kJ/mol, matching deltaG units
- Keep the -160 mV default for c0 unchanged; update comments to state mV
  explicitly on both defaults (e0=0 mV, c0=-160 mV)
- Add warns-only guard (no raise/clamp): WARNING >300 mV (suspicious),
  stronger WARNING >1000 mV (egregiously non-physical)
- Add regression test suite in tests/fbapkg/test_fullthermo_membrane_potential_unit.py
  verifying: default values, mV-scaled constant arithmetic, neutralcharge/e0 edge
  cases, and the two-band warning guard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d-input

feat(annoont): AnnotationOntology.from_prd_input + fix msbuilder get_feature lookup
Author agent-io/roadmaps/energy-loop-analysis.md as the first production
roadmap arc for the modelseedpy project. 6 milestones (m1a/m1b parallel
in group g1, m2-m5 sequential); sourced from the ai-roadmap humanprompt
Example 2 and conforms to the AIAssistant roadmap schema. Parses cleanly
via assistant.state.roadmap.load_roadmap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… record

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parse check: 6 milestones, grouped 2 [g1], status enum ok.
File matches AIAssistant fixture byte-for-byte.
`rpcclient.py:75` does `raise ServerError(**err["error"])` which
requires `err["error"]` to be a mapping. Well-behaved JSON-RPC servers
return a dict there, but tutorial.theseed.org (and other backends
under load) occasionally return a plain string or a list. The
`**` unpack then raises

    TypeError: argument after ** must be a mapping, not str

which masks the real upstream failure and surfaces to callers as a
completely unrelated exception. modelseed-api's failure_watcher has
been auto-filing GitHub issues for this shape.

Fix: check `isinstance(error_body, dict)` before the `**` unpack. When
it's not a mapping, coerce the body into `ServerError("Unknown", 0,
str(body))` - the same shape we already produce for a non-JSON 500 or
a JSON 500 without an `error` key. Callers see a `ServerError` with
the upstream body attached to `.message` instead of a `TypeError` with
no context.

5 unit tests in tests/core/test_rpcclient.py:
- dict error body -> populated ServerError (happy path)
- string error body -> Unknown ServerError with body in message (regression)
- list error body -> same shape as string
- 500 with no `error` key -> pre-existing Unknown branch, locked in
- 500 with non-JSON content-type -> pre-existing branch, locked in

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(rpcclient): handle non-dict error bodies from JSON-RPC 500 responses
…rmoPkg

Extends FullThermoPkg.build_concfit_objective with an optional per-metabolite
weights dict (weights=None reproduces the pre-change unweighted objective
exactly) and adds a new build_concfit_objective_l2 builder for a quadratic
weighted-least-squares fit, guarded to require a QP-capable optlang backend
(gurobi/cplex) since GLPK cannot hold a quadratic objective. build_package
gains concfit_objective_type ("l1"|"l2", default "l1") and concfit_weights
(default None) to dispatch between them, and now raises a clear ValueError
(not KeyError) when set_concfit_objective=True but target_concentrations is
absent. Implements Modules 1-3 of
agent-io/prds/tmfa-weighted-concfit/fullprompt.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`run_atp_correction` ends `return self.build_tests()` -- that is, AFTER
`expand_model_to_genome_scale` has applied the ATP expansion filter by zeroing
one direction of each cycle-forming reaction. `build_tests` opened with a
truthiness test on `selected_media`, so a model where NO medium qualified
re-entered the "not yet computed" branch, and that branch ends in
`restore_noncore_reactions()` -- which writes `original_bounds` back over
every non-core reaction and so silently undoes the filter.

`selected_media` is initialised to None and `determine_growth_media` sets it
to a list before appending, so an EMPTY list means "evaluated, and nothing
qualified" -- a real answer, not a missing one. `is None` is the check the
guard was always meant to make; truthiness conflated the two states.

Measured on KBDL builds: `rxn08941_c0` went from blocked back to
[-1000, 1000] and free ATP from 0 to 1000, returning energy-generating cycles
to a delivered model with no error and no warning.

This fires for any genome that ends up with zero selected media, so it is
latent in every caller of `run_atp_correction`, modelseed-api included -- not
only in the KBDL path that exposed it.

Also drop a stray line in `MSModelUtil.get_atputl` that built a correctly
configured `MSATPCorrection` and then immediately overwrote it with
`MSATPCorrection(self.model)`, discarding `core_template`, the ATP media path
and the gap-filling settings. Anything reaching ATP tests through
`get_atp_tests` was getting that unconfigured object.

The new regression test applies the filter the way
`expand_model_to_genome_scale` does and asserts it survives `build_tests`,
using the existing infinite-ATP-loop fixture -- the victim it selects is
`Kt2r_c0`, the reaction that forms that fixture's cycle. It needs no solver
result and no local biochemistry database. A second test pins the `None` side,
so narrowing the guard cannot silently disable it. The first fails against the
previous code.

Refs kbaseincubator/KBDLJobRunningPrototype#28 (cause 2).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Brings in AnnotationOntology.from_prd_input plus the msbuilder get_feature
lookup fix (#26), and non-dict JSON-RPC 500 error-body handling in rpcclient
(#27). Neither touches msatpcorrection.py or msmodelutl.py, so the ATP-safety
fix in c49e958 is unaffected; git merge-tree previewed this as conflict-free
before it was run.

Merging so that main carries both halves before it is pushed: GitHub main was
at b758b44, which still carried the build_tests bug that c49e958 fixes, and
that commit is what the modelseed-api Docker image pins.
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.

5 participants