Skip to content

refactor(fl)!: complete the NVFLARE Client-API migration — spleen tutorial ported, legacy Executor syntax retired, job types renamed, fed_opt migrated - #983

Merged
atriaybagur merged 13 commits into
developfrom
665-spleen-client-api-migration
Aug 18, 2026
Merged

refactor(fl)!: complete the NVFLARE Client-API migration — spleen tutorial ported, legacy Executor syntax retired, job types renamed, fed_opt migrated#983
atriaybagur merged 13 commits into
developfrom
665-spleen-client-api-migration

Conversation

@atriaybagur

@atriaybagur atriaybagur commented Aug 17, 2026

Copy link
Copy Markdown
Member

Completes the NVFLARE Client-API migration tracked by #665: ports flip-fl-base#116 (spleen tutorial → Client API), then retires the legacy Executor syntax entirely — the Client-API tutorials, templates and job types take over the plain names, converging NVFLARE's job-type naming with Flower's.

1. Spleen tutorial migrated to the Client API (flip-fl-base#116)

  • New fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation (Client API): job.py wires FlipFedAvgRecipe (FLIP server components, analytics bridge, VAL_DICE best-model selection); a single trainer.py replaces the FLIP_TRAINER/FLIP_VALIDATOR executor pair (train + evaluate tasks); metrics ride SummaryWriter tags through FlipAnalyticsBridge; models.py stays checkpoint-compatible with the evaluation tutorial; the trainer fails loudly when no image/label pair is found, naming the data-enrichment step.
  • The half-landed vanilla-FedAvgRecipe port (client.py/job.py/model.py at the old tutorial root) is superseded and removed.

2. Legacy Executor tutorials retired; Client-API tutorials renamed to the plain names

  • Removed the Executor variants of xray_classification, 3d_spleen_segmentation, 3d_spleen_segmentation_evaluation and latent_diffusion_model; the *_client_api directories were renamed onto the plain names (preserving the legacy dirs' unique assets: xray + spleen bundle-export configs, the spleen utils/ download tooling + pyproject.toml).
  • The legacy Docker testing/ harness (app_organiser.sh + compose simulator) and the test-template target are removed — every tutorial now runs via its job.py on SimEnv (make runmake sim) from the flip-utils venv.

3. Job types renamed: *_client_api → the plain names (breaking)

  • fl-apps/nvflare/{standard,evaluation,diffusion_model} are now the recipe-generated Client-API templates (legacy Executor templates deleted); the aggregate required_files.json is regenerated — standard no longer requires validator.py, evaluation now requires models.py.
  • Runtime aliases are kept for pre-rename models: evaluation_client_api in flip-api's results handling and standard_client_api in flip.export.bundle's exportable set.
  • Breaking for external apps: an upload whose config.json still says "job_type": "standard_client_api" / "evaluation_client_api" is now rejected (not in the manifest). In-tree apps (incl. the Ark+ tutorials) are updated; the FLIP-Ark repo's app configs need the same one-line change before the next stag deploy.
  • flip-ui demo mocks updated to mirror the new manifest.

4. fed_opt migrated to the Client API too

  • New flip.nvflare.recipes.FlipFedOptRecipe (+ FlipFedOptShareableGenerator, stock PTFedOptModelShareableGenerator extended to source the model from the user's models.get_model). This is the layout NVFlare's own Client-API FedOptRecipe (nvflare.app_opt.pt.recipes.fedopt) wires — stock SAG + WEIGHT_DIFF aggregator + the FedOpt shareable generator, with the upstream note "FedOpt is only implemented for TransferType.DIFF and DataKind.WEIGHT_DIFF in the aggregator" — with FLIP's forked SAG and server components swapped in, exactly as FlipFedAvgRecipe relates to upstream's FedAvgRecipe. The client contract is identical to standard, so every standard app runs under fed_opt unchanged; required_files drops validator.py.
  • Server-optimizer defaults follow stock NVFLARE FedOpt (SGD lr=1.0, momentum=0.6, no scheduler) rather than the retired template's Adam-at-0.5 — that setting was never live (see the guard bug below) and, once the guard is fixed, demolishes the global model in a single step (verified: round-1 Dice pinned at 0.0000 under it).
  • Bug fix in flip.nvflare.controllers.ScatterAndGather: the FedOpt guard in _diff_to_weights tested isinstance(self.aggregator, PTFedOptModelShareableGenerator) — never true for any real config (that class is a shareable generator, not an aggregator) — so the DIFF→WEIGHTS reconstruction ran unconditionally and fed the FedOpt aggregator full weights it then rejected: the legacy fed_opt job type never actually aggregated. The fixed guard keys on the aggregator's expected_data_kind, handling both its shapes (plain DataKind when hand-constructed, the {dxo_name: DataKind} dict it is normalised to at runtime — the subtlety that made the first fix attempt miss). The unit test that mirrored the bug (its stub subclassed the generator to satisfy the isinstance) is rewritten against the realistic dict-form aggregator.
  • Note: flip-utils changes reach deployed stacks via the FL image rebuild (make build-fl), the standard flip-utils shipping path.

5. Aggregation aligned with stock NVFLARE: the DIFF→WEIGHTS bridge is gone

Git archaeology (prompted by review discussion) showed ScatterAndGather._diff_to_weights was never a design requirement: it came in with the pre-open-source import as a bridge between DIFF-sending trainers and the legacy templates' WEIGHTS-expecting aggregator, and the later "Fix fed-opt issues" commits tried (and failed — the isinstance guard) to carve FedOpt out of it. Stock NVFLARE needs none of this: the aggregator's stock default expects WEIGHT_DIFF, averages the diffs directly, and FullModelShareableGenerator adds the average onto the global model — mathematically identical for FedAvg (avg(g+dᵢ) = g+avg(dᵢ)) and natively partial-safe for frozen-backbone head-only updates (keys absent from the diff keep their global value — the exact guarantee FLIP#684's reconstruction existed to provide).

  • _diff_to_weights is deleted; every template (standard, evaluation, diffusion_model, fed_opt) now wires a bare InTimeAccumulateWeightedAggregator() (stock default) and aggregates client diffs directly. The FedOpt-specific guard becomes unnecessary and is gone with it.
  • fl-api's config assembly only ever injects aggregation_weights into the aggregator (never expected_data_kind), so the platform path is untouched.
  • Deploy coupling: the new templates and the new flip-utils must ship together — the FL images are rebuilt (make build-fl) whenever flip-utils changes, which is the standard shipping path, so this is the normal release flow rather than an extra step.
  • Cosmetic: the last _client_api strings (simulator workspace defaults in the tutorials' job.py/Makefiles and the round-metrics script) are renamed away.
  • Follow-on fix caught by running the Ark+ tutorials: both Ark+ evaluation Makefiles passed an explicit --workspace ../../testing/runs/workspace to job.py, pointing into the removed harness dir — the override is dropped so job.py's /tmp/nvflare/<name> default applies, like every other tutorial.

Verification

Unit/static: fl-apps/check_required_files.sh regenerated + clean; ruff clean across the touched trees (the NVFLARE tutorial tree's pre-existing lint debt went away with the legacy files); flip-utils 732 unit tests (incl. new FlipFedOptRecipe tests + the rewritten SAG guard test), flip-api 1501, fl-api-base 149, imaging-api untouched-by-this-PR.

Local simulator (RTX 5090, MSD spleen):

  • Renamed spleen tutorial (make -C fl-tutorials run-tutorial TUTORIAL=3d_spleen_segmentation): clean end-to-end run at the committed config.
  • FlipFedOptRecipe driven with the spleen tutorial's unmodified app_files (2 rounds): zero aggregator rejections and healthy dynamics — round-0 VAL_DICE 0.012→0.031, a normal post-aggregation dip (0.018) with recovery, final global TEST_DICE 0.0167. Under the old guard the same run logged expected WEIGHT_DIFF but got WEIGHTS and rejected every contribution.
  • make export output for the renamed standard template matches the committed configs exactly.

Stock-aggregation verification — every job type, zero aggregator rejections everywhere:

Path Run Result
standard spleen tutorial sim, 2 rounds × 2 sites ✅ final-global TEST_DICE 0.0504
fed_opt spleen app under FlipFedOptRecipe, 2 rounds ✅ final-global TEST_DICE 0.0609
head-only / frozen-backbone spleen + aggregate_only_regex, 2 rounds ✅ KeepOnlyVars → partial-diff aggregation → TrimBroadcastVars → ReconstructFullModel all engaged; TEST_DICE 0.0140
diffusion_model LDM tutorial, 1 AE + 1 DM round × 2 sites ✅ both stages aggregated (18 train_ae + 18 train_dm tasks)
evaluation Ark+ baseline evaluation tutorial ✅ real per-lesion AUROC output (Effusion ≈1.0)
evaluation (multi-model) Ark+ multimodel evaluation tutorial ✅ per-model AUROC output
standard + SERVER_CHECKPOINT + head-only Ark+ fine-tuning tutorial (2.47 GB Swin backbone, AGGREGATE_ONLY_REGEX=omni_heads) ✅ 3 rounds × 2 sites, zero rejections; full filter chain in the exported job (KeepOnlyVars+PercentilePrivacy → TrimBroadcastVars → ReconstructFullModel) over the bare stock aggregator; final per-lesion TEST precision 0.97–1.0 / recall 1.0
Platform e2e (standard) dev stack on FL images rebuilt from this branch ✅ upload → bundle → train → RESULTS_UPLOADED, final-global val_acc 0.0602 (model 2149843f), best+final checkpoints in the zip, zero rejections on the live fl-server

Dev-stack e2e (platform path, GSTT, NVFLARE backend):

  • Original migration evidence (pre-rename, same code): both e2e smokes green on the same 21-study project; old-vs-new Dice at identical 10-round budget: legacy final-global val_acc 0.0063 vs Client-API 0.5217 (local sim: 0.060 vs 0.1875), with best_FL_global_model.pt present in the Client-API results zip.
  • Post-rename re-verification on a live stack serving this branch's fl-apps: the renamed spleen tutorial uploaded with "job_type": "standard" went through upload → scan → bundle (new manifest) → train → RESULTS_UPLOADED (model 5de08634), final-global val_acc 0.0132 at the committed 2-round config, with best_FL_global_model.pt in the results zip (the fl-api's submit-time selector injection works through the renamed manifest).

Multi-agent review round (applied in-branch)

A six-pass review (code, tests, comments, silent failures, type design, security) ran against this branch; every confirmed finding is fixed in the six commits after faf2aff2:

  • FedOpt stepped on a second fresh-init model copy — stock's generator promotes its own state_dict() as the global and never syncs from the persistor, so round 1 silently replaced the global (any SERVER_CHECKPOINT backbone included) with random init + one step. The generator now syncs its copy from the live global in place before each step (optimizer state preserved via tensor identity), resolves source_model at START_RUN under system_panic (the dispatcher swallows handle_event exceptions), and the recipe gained stock's ensure_config_type_dict normalisation + isolated defaults + a construction-time shape check.
  • Silent FULL-sender no-op killed — a round accepting zero client results now aborts the job loudly on BEFORE_AGGREGATION (previously: empty aggregates applied as no-ops to a COMPLETED "trained" model), a wrong weights kind relays an actionable params_type='DIFF' message to the hub, and every failed client task is relayed (pointed fallback naming the trust-side logs when no exception header exists — full traceback transport is Restore end-to-end client exception messages to the hub for Client-API tasks #987).
  • Pre-rename *_client_api aliases actually work now — they were unreachable dead code (manifest validation rejected them first); both bundlers normalise them before validation, UnknownJobTypeError names the valid set + fix, and the contract is pinned unmocked against the real manifest.
  • Legacy retirement completedModelEval, the executors/ package and EvaluationPTModelLocator (all unwired) deleted with their tests; referencing prose reworded historically.
  • Template drift guard — new tests pin each committed fl-apps template equal to its recipe's export and assert no deployed aggregator overrides the stock WEIGHT_DIFF default.
  • ~25 confirmed doc/comment rot items swept — including the standard README instructing the now-rejected DataKind.WEIGHTS return and nonexistent flare.get(), the .gitattributes linguist-generated globs that matched nothing post-rename, and validator.py lingering in the UI fallback/mock/docs contracts.

The security pass found no vulnerabilities introduced by the PR (the one candidate sink — user-steerable server-side instantiate_class — was traced end-to-end and is not reachable from user input).

Issues found and split out during verification

Ticks the flip-fl-base#116 box on #665 and resolves its "retire the legacy standard Executor job type once parity is confirmed" consideration — parity was confirmed and the legacy syntax retired. The XGBoost port (flip-fl-base#114) remains the last open #665 item.

…e NVFLARE Client API

Port of flip-fl-base#116 into the mono-repo (#665), following the layout the
xray_classification_client_api migration established:

- New image_segmentation/3d_spleen_segmentation_client_api/ tutorial driving
  the standard_client_api job type: job.py wires FlipFedAvgRecipe (so the FLIP
  server components, analytics bridge and best-model selection all apply),
  and app_files/trainer.py replaces the legacy FLIP_TRAINER/FLIP_VALIDATOR
  executors with a single nvflare.client script (train + evaluate tasks).
- models.py matches the evaluation_client_api spleen variant, so the state
  dict stays checkpoint-compatible with the legacy and evaluation tutorials.
- Training metrics ride SummaryWriter tags (TRAIN_LOSS/VAL_LOSS/VAL_DICE
  @epoch, TEST_DICE) through FlipAnalyticsBridge; the trainer sends a DIFF
  update which FLIP's ScatterAndGather reconstructs into full WEIGHTS.
- The trainer fails loudly when no image/label pair is found, naming the
  data-enrichment (label upload) step instead of dying with num_samples=0.
- Removes the half-landed vanilla-FedAvgRecipe port (client.py/job.py/
  model.py/transforms.py at the legacy tutorial root): it bypassed the FLIP
  platform integration (no FLIP components, vanilla WEIGHT_DIFF aggregation)
  and was superseded by this variant; the legacy Executor tutorial itself is
  unchanged and stays the standard-job-type reference.
- Docs: component-fl-nodes tutorial list gains the new variant.

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
@atriaybagur atriaybagur linked an issue Aug 17, 2026 that may be closed by this pull request
8 tasks
@github-actions github-actions Bot changed the title feat(fl-tutorials): migrate the 3D spleen segmentation tutorial to the NVFLARE Client API Migrate open NVFLARE tutorial PRs from archived flip-fl-base into FLIP Aug 17, 2026
… tutorials, templates and job types take the plain names

Follows the user decision on #665's open consideration: parity between the
Executor and Client-API paths is confirmed, so the legacy syntax goes.

- Tutorials: the Executor variants of xray_classification,
  3d_spleen_segmentation, 3d_spleen_segmentation_evaluation and
  latent_diffusion_model are removed and the *_client_api directories renamed
  onto the plain names (preserving the legacy dirs' unique assets: bundle
  export configs, the spleen utils/ download tooling + pyproject). The legacy
  Docker testing/ harness and the test-template target go with them — every
  tutorial now runs its job.py on SimEnv from the flip-utils venv.
- Job types: fl-apps/nvflare/{standard,evaluation,diffusion_model} are now
  the recipe-generated Client-API templates; the aggregate manifest is
  regenerated (standard drops validator.py, evaluation gains models.py).
  BREAKING for uploads still declaring *_client_api job types; runtime
  aliases are kept where pre-rename models are read back (fl_service.py
  evaluation handling, flip.export.bundle). flip-ui demo mocks updated.
- fed_opt migrates too: new FlipFedOptRecipe + FlipFedOptShareableGenerator
  mirror NVFlare's own Client-API FedOptRecipe layout (stock SAG +
  WEIGHT_DIFF aggregator + PTFedOptModelShareableGenerator) with FLIP's
  components; defaults follow stock NVFLARE FedOpt (SGD lr=1.0 momentum=0.6)
  — the retired template's Adam-at-0.5 was never live and destroys the model
  once aggregation works. Client contract identical to standard.
- Bug fix (flip.nvflare.controllers.ScatterAndGather): the FedOpt guard in
  _diff_to_weights isinstance-checked the aggregator against the FedOpt
  shareable-GENERATOR class — never true — so DIFF→WEIGHTS reconstruction ran
  unconditionally and the legacy fed_opt job type never actually aggregated.
  The fixed guard keys on the aggregator's expected_data_kind, covering both
  its runtime shapes (plain DataKind and the normalised {name: kind} dict).
- Docs (component-fl-nodes, flip-utils README/overview, template READMEs,
  CLAUDE.md/AGENTS.md) rewritten for the converged naming.

Verified: flip-utils 732 / flip-api 1501 / fl-api-base 149 tests green; ruff
clean; renamed spleen tutorial + FlipFedOptRecipe (zero rejections) green on
the local simulator; renamed 'standard' job type green end-to-end on the dev
stack (upload → bundle → train → RESULTS_UPLOADED, best+final models).

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
@atriaybagur atriaybagur changed the title Migrate open NVFLARE tutorial PRs from archived flip-fl-base into FLIP refactor(fl)!: complete the NVFLARE Client-API migration — spleen tutorial ported, legacy Executor syntax retired, job types renamed, fed_opt migrated Aug 17, 2026
@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…lution

Codecov flagged the new component's handle_event as the only uncovered lines
of the fed_opt migration: dict-config resolution to a live module, the
missing-path panic, the string-id passthrough, and resolve-once semantics
(the server optimizer state lives on the resolved model's parameters).

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
…sh-init copy

Review finding (critical): FlipFedOptShareableGenerator's dict-config source_model
instantiates its own model object, independent of the persistor instance whose
state_dict seeds the round-0 global. Stock PTFedOptModelShareableGenerator never
syncs its copy from the global - it promotes self.model.state_dict() as the new
global after every optimizer step - so from round 1 the global silently became
fresh_random_init + lr*avg(diff): any SERVER_CHECKPOINT backbone and every frozen
parameter was replaced with random weights. Invisible in the spleen sims (no
checkpoint, so the run just restarts from the copy's init) - exactly the runs the
tutorial-less fed_opt job type would never exercise.

shareable_to_learnable now loads the live global into the copy in place before
delegating to stock (load_state_dict copies into the existing tensors, so the
optimizer built at START_RUN keeps its parameter references and momentum state;
from round 2 the sync is a value-identical no-op).

Also hardened to review findings on the same seam:
- dict resolution is gated on START_RUN (where stock consumes source_model) and
  instantiate_class is wrapped in system_panic - the event dispatcher swallows
  handle_event exceptions, so a typo'd path or an ImportError inside the user's
  models.py previously wasted a full training round and then died as an opaque
  NoneType server error (stock wraps its own optimizer resolution for exactly
  this reason);
- the recipe now mirrors stock FedOptRecipe's ensure_config_type_dict
  normalisation (a user dict without config_type='dict' made the server
  ComponentBuilder instantiate torch.optim.* at config-load time, sans params),
  deep-copies the module-level default (stock mutates optimizer_args['args'] in
  place at START_RUN, which would have poisoned the process-wide default), and
  rejects path-less optimizer_args at construction;
- docstring corrections: the FedAvg hook applies the aggregated WEIGHT_DIFF (not
  WEIGHTS), the phantom _aggregator_data_kind cross-reference is gone, the CPU
  device default is FLIP's own choice (stock is cuda-if-available), and the
  claim that dict model refs import at recipe construction was empirically false
  (they resolve lazily - the fedopt recipe tests now run with no models stub).

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
…s loudly

Review finding (critical): with the DIFF->WEIGHTS bridge gone, a Client-API app
sending params_type='FULL' - NVFLARE's default when params_type is omitted - had
every contribution rejected by the WEIGHT_DIFF aggregator at log-only severity,
each round aggregated an empty diff and applied it as a no-op, and the job ended
COMPLETED with a model bit-identical to round 0 while the analytics bridge kept
streaming healthy local loss curves. A regression in visibility terms against
the deleted bridge, whose WEIGHTS-expecting aggregator ACCEPTED full sends.

ScatterAndGather now:
- panics on BEFORE_AGGREGATION when the finishing round accepted zero results
  (acceptance counts are final there and the empty aggregate has not yet been
  applied; there is no legitimate run where every client's update is rejected),
  relaying the abort to the hub first;
- reports an aggregator-incompatible weights kind to the hub once per round with
  an actionable message naming the FLModel(params=diff, params_type='DIFF')
  contract, instead of stock's server-log-only rejection line;
- relays EVERY failed client task to the hub, falling back to a pointed message
  naming the return code and the trust-side fl-client logs when no 'exception'
  header is present - the retired legacy executors were the only components that
  ever set that header, so the old EXECUTION_EXCEPTION-with-header-only relay
  had become dead code on the Client-API path (a Client-API script that dies
  pre-flare.init returns a bare TASK_ABORTED). Full traceback transport is
  FLIP#987.

Also retires the stale _diff_to_weights references the review found in the
controller test files (a vacuously-true inheritance parametrize entry, a test
named for the deleted reconstruction, and the file header).

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
…ifest validation

Review finding (critical, three reviewers independently): the kept
'evaluation_client_api' alias tuple was unreachable dead code - is_valid_job_type
rejects retired names against the renamed manifest 66 lines earlier, and the
alias template directory no longer exists - so every model created before the
rename hard-failed at training start with UnknownJobTypeError, directly
contradicting the documented 'aliases survive for pre-rename models' contract.
The unit test that appeared to cover the alias passed only because it mocked
is_valid_job_type to True and fabricated an alias base tree.

Both bundlers now resolve {standard,evaluation,diffusion_model}_client_api to
the plain names BEFORE validation (single _normalise_job_type helper), the
checkpoint-divert branch drops the now-redundant alias tuple, and
UnknownJobTypeError names the valid set plus the fix (edit config.json and
re-upload) - it surfaces at training start, long after upload/scan/approval all
succeeded.

Tests: the alias contract is now pinned UNMOCKED against the real in-repo
manifest (aliases resolve; plain names pass; the retired keys are genuinely
absent - the breaking manifest change itself; unknown names get the remediation
message), the eval-divert test proves the alias path bundles from the plain-name
template dir, and the fixture manifest mirrors the real post-rename contract
instead of teaching the retired file sets.

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
…ired Executor-era classes

Review follow-through on the full-retirement goal: ModelEval (the bespoke
evaluation controller), the flip.nvflare.executors package (RUN_TRAINER /
RUN_VALIDATOR / RUN_EVALUATOR) and EvaluationPTModelLocator (the multi-model
COLLECTION locator paired with ModelEval) were left behind unwired - no shipped
template or config references any of them since the Client-API templates took
over, and git archaeology confirmed no external consumer. Their tests go with
them.

Referencing prose is reworded historically: the EvaluationJsonGenerator keying
docstring labels the no-MODEL_OWNER branch as the retired controller's stored
shape, the eval recipe module docstring speaks of the legacy flow in the past
tense and describes the live per-model broadcast accurately (the locator serves
every entry in config.json['models'] - one single-model validate task per
(model, client), not 'a single uploaded model'), and the Ark+ process_tools
docs now attribute the strict load to the client-side evaluator rather than the
deleted locator.

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
…rift

Review finding: production deploys the COMMITTED template JSONs (fl-apps/ baked
into the flip-api image) while every unit test exercised the recipes - nothing
regenerated recipe.py output and compared, so a template whose aggregator
regained expected_data_kind='WEIGHTS' would pass the whole suite while every
live client contribution is rejected and the 'trained' model never leaves
round 0 (the dice-0.006 class of bug this PR fixes).

One test per (template, artefact) now asserts the committed
config_fed_server.json / config_fed_client.json / meta.json equal the recipe's
export (parsed-tree comparison; the filter entries' set-derived 'tasks' lists
are the one hash-seed-ordered field and are canonicalised), plus a direct
assertion on the deployed artefacts that no aggregator overrides the stock
WEIGHT_DIFF default.

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
Comment-accuracy review findings, all verified against the code before editing:

- standard template README told app authors to return DataKind.WEIGHTS (now
  rejected by the aggregator) and to call flare.get() (does not exist) - the
  upload contract is FLModel(params=diff, params_type='DIFF') / flare.receive();
  workflow ids aligned with the generated config.
- diffusion README + LDM trainer docstring still described the deleted
  server-side DIFF->WEIGHTS rebuild; both now state stock diff aggregation.
- flip-utils README claimed the legacy Executor job types 'remain supported'
  (they cannot run) and, with overview.rst, that diffusion ships a tracked
  custom/ (no template does); overview.rst contradicted its own JobType table;
  the deleted executors/ package is out of both trees.
- component-fl-nodes.rst had the rename antecedent inverted (the Client-API
  templates were the ones briefly under *_client_api names).
- required-files rot: the fed_opt demo mock, the /model/job-types docstring
  example, flip-ui's API-failure fallback (service + spec + docs-GIF spec +
  seed data) and the MAP-boundary diagram all still carried validator.py for
  job types that no longer require it.
- .gitattributes linguist-generated globs matched nothing after the rename
  (repointed at the plain-name recipe outputs); .gitignore and
  .env.development.example referenced the removed testing/ harness; the last
  PATH_TO_APP leftover is gone; the gutted arkplus fine-tuning Makefile comment
  reads again; fl-tutorials/Makefile no longer claims Flower is pending; the
  spleen .env.app comment's argparse claim did not hold on the sim path; the
  spleen eval README table cell states per-model semantics; the spleen
  trainer's NUM_STEPS_CURRENT_ROUND now documents the all-epochs count the DP
  clip bound scales with.

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
…api-migration

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>

# Conflicts:
#	AGENTS.md
#	CLAUDE.md
#	fl-tutorials/nvflare/image_classification/xray_classification_client_api/app_files/transforms.py
#	fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/app_files/validator.py
#	fl-tutorials/nvflare/image_segmentation/3d_spleen_segmentation/client.py
#	fl-tutorials/nvflare/image_synthesis/latent_diffusion_model/app_files/trainer.py
#	fl-tutorials/nvflare/image_synthesis/latent_diffusion_model/app_files/transforms.py
#	fl-tutorials/nvflare/image_synthesis/latent_diffusion_model/app_files/validator.py
An OK result with no DXO payload makes _report_data_kind_mismatch's probe
raise; the new test pins the contract that the failure is demoted to a
debug log and never blocks acceptance — the aggregator stays the authority
on rejection. Closes the 2-line patch-coverage gap Codecov flagged.

Signed-off-by: at24_bioeng625-pc <alexandre.triay_bagur@kcl.ac.uk>
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.

Migrate open NVFLARE tutorial PRs from archived flip-fl-base into FLIP

3 participants