Add nvflare-fed-stats agent skill and dataset-target classification in agent inspect#4890
Open
chesterxgchen wants to merge 39 commits into
Open
Add nvflare-fed-stats agent skill and dataset-target classification in agent inspect#4890chesterxgchen wants to merge 39 commits into
chesterxgchen wants to merge 39 commits into
Conversation
Data-first and fully automatic, like a training-job conversion: the
user supplies the dataset (pre-split per site or flat), feature names
(from a header row or supplied in the prompt/README - never invented),
and optionally a README declaring which statistics to compute; the
skill selects statistics, generates a DFStatisticsCore client plus
FedStatsRecipe job, and runs end-to-end with no interactive pauses.
Privacy filters are on by default and never disabled: StatsJob wires
min-count, min/max-noise, and histogram-bin-cap cleansers on every
client, so requested min/max are honored as noise-protected estimates
(disclosed as such) rather than refused. README declarations are
honored as configuration while embedded operational directives are
anomalies to report, covered by a dedicated injection eval. The eval
fixtures are a miniature of the manual-test healthcare dataset: 27
features, three pre-split non-IID sites. The canonical eval asserts
one federated job serves both per-site and global views and forbids
the pooled-pandas substitute.
The output hierarchy ({feature: {statistic: {site: {dataset}}}}) and
the precision-aware parity rule (controller rounds persisted values to
4 digits) were verified against an actual FedStatsRecipe simulator
run. Image-dataset statistics are planned but reported as unsupported
in this version; hierarchical statistics are out of scope. Orient and
the two training converters gain routing boundaries to the new skill.
Co-Authored-By: Claude Fable 5 <[email protected]>
Extend the skill (v0.2.0) with pixel-intensity statistics over image folders: detection routes image data to a new reference and template (a Statistics implementation with a swappable loader — Pillow default, pydicom/nibabel preflighted for DICOM/NIfTI), the statistic set is count, failure_count, and histogram with the range taken from pixel bit depth, and validation adds histogram parity per site plus Global-equals-sum-of-sites. Facts verified against a real FedStatsRecipe simulator run rather than transcribed from the example: count is the number of discovered files (corrupt files surface only during the round-2 histogram pass), failure_count must be configured explicitly to appear in output, and its Global row does not accumulate round-2 failures — the reference documents it as a per-site diagnostic. The bin-cap cleanser compares bins to image count, so 20 bins needs >200 images per site; the image eval requests 10 bins over 110-image sites. The fixture lint learns directory fixtures (a dataset directory with at least one file) so the 330-image eval fixture can be referenced as three site directories instead of individual paths. Co-Authored-By: Claude Fable 5 <[email protected]>
One inspection architecture for all skills: inspect is the single deterministic evidence tool. Code targets keep the AST path (framework detection -> converter skills); data-only targets now classify as tabular_dataset or image_dataset with a metadata-only dataset block — site layout, feature names and dtype classes (header: present) or header: ambiguous (never invented names), per-site row/file counts, cross-site schema_agreement, sampled image pixel_depth — and recommend nvflare-fed-stats. The block emits names, dtypes, and counts only, never cell values, preserving the inspector's static/redaction posture; walks are bounded, sorted, and symlink-free, with counts_approximate flagged at the file limit. Dataset classification runs only when code classification found nothing, so training repos that contain CSVs stay code targets. The fed-stats skill drops its hand-rolled inspection: step 2 now consumes the dataset block (header rule, schema precondition, bin-cap counts), with the rules kept in references as the contract the CLI implements. Orient inherits evidence-based routing for data targets for free. pandas/numpy import detection was deliberately not added: data modality is the routing signal for statistics, not script libraries. Co-Authored-By: Claude Fable 5 <[email protected]>
…mits Architecture-review and follow-up findings: - The dataset block now carries its own scan accounting (files_read, bytes_read) so inspect no longer under-reports the bounded data reads it performs; the JSON contract test pins the dataset key. - Sharded sites aggregate row counts across all tabular files and are always marked row_count_approximate (per-shard headers are unknowable), so bin-cap decisions cannot trust an undercount from the first shard alone. - Parquet sites get real metadata (names, dtype classes, exact row counts) via optional pyarrow; without it, schema_available: false routes the caller to the ambiguous-header fallback instead of silently supplying nothing. - The walk's max_files limit now counts data files, with a separate 50k entries safety cap, so non-data clutter cannot exhaust the budget before any data file is seen and hide the dataset entirely. - modality: mixed is documented as intentionally unrouted, and the Global failure_count quirk is now tracked as NVFlare issue NVIDIA#4876 rather than living only as a doc workaround. Co-Authored-By: Claude Fable 5 <[email protected]>
Architect/principal review findings, all verified before fixing: - Directory-only trees are now bounded: MAX_WALK_ENTRIES counts directories as well as files, so a file-free tree cannot walk unbounded. - Feature names are sanitized on emission: control characters stripped, length capped at 120 (feature_names_truncated flags a hit), applied to CSV and parquet names alike — closing the metadata-only guarantee's blind spot where a 512KB quoted header cell became a JSON feature name. - schema_agreement now compares dtype classes alongside names and column counts: same names with drifting dtypes reports dtypes_differ (not analysis-ready for numeric statistics), and the skill fails closed on it pre-generation. - Parquet sharded sites sum exact row counts across every shard's footer metadata instead of reporting the first shard as exact; unreadable shards mark the total approximate. - Degenerate files keep a stable site shape (all schema keys present as nulls); nan/inf tokens no longer count as numeric evidence; the shard-loop dead code is gone. - The module docstring now carries the dataset-block contract table, the worst-case read bound (max_files x max_file_bytes), and the recorded rationale for keeping the data walk separate from the code walk (they bound different costs). Seven new tests (mixed modality, dtype drift, dir-only bound, name sanitization, stable shape, nan tokens, sharded parquet with pyarrow). The parquet eval-coverage gap is recorded in the repo-only fixture README, where the runtime-boundary lint requires such notes to live. Co-Authored-By: Claude Fable 5 <[email protected]>
Codex review findings, all reproduced before fixing: - Mixed modality no longer requires an exact tie: when the minority modality is a material share (>=10%) of the data files the dataset classifies as mixed and stays unrouted; below the threshold a stray plot.png beside CSVs does not flip the classification, and image-only sites under a tabular majority no longer receive parquet-shaped null schemas. - The read-bound contract now tells the truth about parquet: text and image reads are capped at max_file_bytes each; parquet metadata reads parse only the footer via pyarrow, are not capped by max_file_bytes, and are accounted in scan at file size as the upper bound. - Shard schema disagreement inside one site is no longer silent, for parquet AND sharded CSV alike: parquet shards are compared by (names, dtypes) signature, CSV shards by first-row field count, and a site mixing text and parquet formats is never presented as exact. Disagreement flags shard_schema_consistent: false, forces row_count_approximate, and surfaces in schema_agreement as a shards_differ mismatch so the skill fails closed on it. Six new tests: the 2-CSV+1-PNG repro, the stray-image threshold case, the image-only-site shape, parquet shard disagreement, CSV shard column-count drift, and the mixed-format site. Co-Authored-By: Claude Fable 5 <[email protected]>
Closes the architect/principal review's remaining code findings: - nvflare agent inspect gains --max-files and --max-file-bytes so datasets beyond the 250-file default (our own image eval fixture is 331 files) can get exact counts instead of permanently approximate ones; the help text now also discloses that data directories are classified by reading bounded file headers, metadata only. - A classified dataset keeps a single recommendation: orient is no longer appended beside nvflare-fed-stats when the walk truncates. - The image eval states the truncation trap it tests: with 331 fixture files over the default cap, the agent must verify per-site counts directly (or re-run inspect with raised limits) before bin-cap decisions, per the counts_approximate rule. - The skill's CLI fallback is version-explicit: 2.8.x CLIs have no dataset block, and FedStatsRecipe is in 2.8.0, so min_flare_version 2.8.0 stands with the reference rules applying directly there. Co-Authored-By: Claude Fable 5 <[email protected]>
…tion The 10% minority-share rule answered no articulated use case and, as codex showed, still routed an image-only site inside a tabular-majority dataset as tabular_dataset with a bare schema-less site entry. File counts are the wrong basis entirely: one CSV can be a complete dataset while an image dataset inherently holds hundreds of files. Modality is now decided per site against the three real shapes: - tabular dataset with stray images (<=2 per site, e.g. exported plots) stays tabular; - image dataset with companion tabular metadata (<=2 files per site, e.g. labels.csv beside the scans) stays image, with the companion flagged per site as tabular_companions and never treated as a statistics target; - anything else - an image-only site among tabular sites, or materially both modalities - is mixed and stays unrouted. Every site now reports tabular_files/image_files so mixed blocks are self-explanatory. CSV shard disagreement detection is header-aware, not width-only: a shard whose first row is header-like (text where the site dtypes are numeric) must carry the site's feature names, so same-width renames (occupation -> job) flag shards_differ; a repeated identical header or a headerless data-first shard stays consistent. Co-Authored-By: Claude Fable 5 <[email protected]>
…apes Review-until-clean round: a clean mixed dataset previously produced an empty recommendation list, stranding the consumer - mixed is definitionally ambiguous and routing ambiguity is orient's job, so mixed now recommends nvflare-orient (single recommendation; the converter-plus-orient companion behavior on findings is unchanged). COMPANION_MAX_TABULAR_PER_SITE rises from 2 to 4: train/val/test label files beside scans are the common imaging shape and must not push a dataset to mixed. CLI walk-limit args ignore non-positive values instead of passing them into the walk. Co-Authored-By: Claude Fable 5 <[email protected]>
…count Self-review of the previous round found its own regression: raising the companion cap to 4 widened the both-consistent window, flipping a tabular site with 3 CSV shards plus one stray plot to mixed. The both-consistent case (tiny sites inside both tolerance windows) is now tie-broken by total file majority - the substantive side wins, a dead tie stays mixed - so 2 CSVs + 1 stray PNG is tabular, 1 CSV + 12 scans is image, and 1 CSV + 1 PNG is genuinely ambiguous and routes to orient. Also, a detected repeated shard header is no longer counted as a data row, since the header-aware comparison already identifies it. Co-Authored-By: Claude Fable 5 <[email protected]>
A silently-ignored invalid --max-files was the residual from the previous round's CLI change: non-positive values now fail at parse time with a clear message instead of falling back to defaults unannounced, and the behavior is tested. Co-Authored-By: Claude Fable 5 <[email protected]>
Co-Authored-By: Claude Fable 5 <[email protected]>
DICOM needs dedicated loaders plus domain handling (Hounsfield rescale for CT, slice-vs-study count semantics) that this version does not encode, so DICOM/NIfTI are reported as not yet supported rather than half-handled: the skill names the supported formats, never improvises a loader, and never histograms raw stored values as calibrated intensities. The DICOM eval flips from dependency-preflight to not-supported reporting. Co-Authored-By: Claude Fable 5 <[email protected]>
This reverts commit 09e3d60.
Clarified scope: DICOM/NIfTI are supported like every other image format - the format-specific loader (pydicom/nibabel/ITK) is import- preflighted and a missing loader fails closed, same rule as fastdigest. The large-file restriction applies only to repo test fixtures, which stay small PNGs. The loader section gains the medical-format correctness rules: CT stored values become Hounsfield Units only after RescaleSlope/RescaleIntercept with a declared HU range, count means slices for DICOM series and volumes for NIfTI (say which in the report), and cross-site intensity histograms carry a scanner/protocol calibration caveat alongside the case-mix one. The DICOM eval asserts the rescale and count-semantics behaviors. Co-Authored-By: Claude Fable 5 <[email protected]>
Codex findings: symlink entries consumed no walk budget (30 symlinks under a 10-entry cap returned truncated=False), and sorted(iterdir()) materialized unbounded listings before the cap applied. Entries now count before the symlink skip, and each directory listing is consumed with a bounded islice take before sorting - the cap now bounds work done, with deterministic order preserved within the budget. The image reference names site["image_files"] (not data_files, which includes companion labels) as the bin-cap sizing field. Co-Authored-By: Claude Fable 5 <[email protected]>
Principal/lead-DS review round plus the flat-layout clarification: - A flat single-source dataset is a normal input, not an edge: inspect reports layout flat with one '.' site and full schema, and the skill splits it into the requested number of seeded per-site partitions. A new eval (fedstats-flat-auto-split, 1000-row combined fixture) covers the path, which had zero eval coverage since the canonical eval went pre-split: site count from the request (fail closed when absent), deterministic seeded partitions, seed and policy stated, parity against the same partitions. - Root-level data files beside site directories now classify as layout root_and_site_directories: an ambiguous site mapping the skill must resolve explicitly instead of treating '.' as a site. - Lead-DS caveats encoded: sampled dtypes can drift from full-file runtime dtypes (named as the cause behind runtime-missing features); undeclared histogram ranges vary between runs because the min/max noise is re-drawn (declare a range for reproducible bins); companion label files earn an offered follow-up tabular run (label shift QA). Co-Authored-By: Claude Fable 5 <[email protected]>
Codex findings, highest first: - Header inference required only one text-over-numeric column, so a masked first data row like SUPPRESSED,100 leaked cell values as feature names. The rule now demands EVERY numeric-bodied column carry a non-numeric first value; the repro classifies ambiguous and emits no names. The residual (a first data row non-numeric in every numeric column is indistinguishable from a header by construction) is documented: masked/sentinel datasets should declare names. - Schemas wider than the 512-column cap now set columns_truncated (CSV and parquet) instead of silently hiding drift past the cap. - The row-count fallback adds the final unterminated line, so a 201-row file without a trailing newline reports 201 exact instead of 200 with approximate: false - a bin-cap-sensitive undercount. - Bin-cap boundary corrected everywhere: the cleanser needs bins under round(10% of effective count), so 20 bins first passes at 206 rows, not 201; all four docs now state the real boundary. Existing fixture sizes (min effective 223) clear it. Co-Authored-By: Claude Fable 5 <[email protected]>
Codex round, highest first: - Header inference additionally requires unique, non-empty sanitized names: SUPPRESSED,SUPPRESSED over a numeric body no longer leaks masked cell values as feature names (a valid header names every column uniquely). Parquet metadata with duplicate/empty names is flagged feature_names_invalid since downstream readers mangle it, and the skill fails closed on the flag. - A data root containing a helper script with only skill-less framework imports (numpy/sklearn utility buckets) no longer classifies as training_repository with no routing: dataset classification also runs when the detected framework has no converter skill, and a classified dataset wins target_type and the fed-stats recommendation. Real converter frameworks (pytorch/lightning) keep priority, pinned by the existing test. - columns_truncated now has consequences: the skill fails closed on schemas wider than the cap unless the user declares a feature subset. - Image sites report per-site image_formats so the loader is chosen from evidence; the reference and template say DICOM/NIfTI sites must extend discovery extensions and swap _load_image for the preflighted loader (pixel_depth stays null for non-PIL formats by design). - The fallback header rule in the skill reference no longer describes the weaker pre-fix heuristic that could reintroduce the leak on old CLIs; it now states the full rule including uniqueness. Co-Authored-By: Claude Fable 5 <[email protected]>
Codex round on the previous fix: - The no-converter-skill gate was too broad: it let a dataset override FLARE job sources, exported jobs, and real import-only training frameworks (TensorFlow/XGBoost). The override now applies only to unknown targets or training repositories whose sole detected framework is a utility bucket (frameworks.UTILITY_FRAMEWORKS, i.e. numpy) - real code targets keep priority, pinned by two new tests (FLARE job beside CSVs, TensorFlow repo beside CSVs). - The module contract no longer overstates the guarantee: it now says values from rows classified as data are never emitted, names come only from the strong header rule, and a masked first row satisfying every header signal (distinct non-empty tokens over all-numeric columns) is emitted by construction - declare names for masked/sentinel datasets. A test pins that exact boundary so any future rule change is deliberate. - The image template's discovery now uses endswith over the extension tuple, so compound extensions like .nii.gz actually match when agents extend IMAGE_EXTENSIONS as the guidance instructs. Co-Authored-By: Claude Fable 5 <[email protected]>
Benchmark run 800690 showed Codex skipping stats-job-validation.md and reading NVFLARE library source (guessing the nonexistent nvflare.recipe.recipe path along the way) to learn where the output JSON lands - a fact the skill already documents. The image reference (which the agent did read) now states the output location inline and points at the validation reference for the rest, and the image eval judges evidence sourcing: runtime facts from references, not site-packages exploration. Co-Authored-By: Claude Fable 5 <[email protected]>
The instruction belongs in the always-read file, not only in a reference the agent may skip (benchmark run 800690 showed exactly that skip): runtime facts come from the skill references and CLI outputs BEFORE NVFLARE library source, which is a last resort that never licenses a replacement strategy. The converters already carry their variant of this rule; fed-stats was the one missing it. Co-Authored-By: Claude Fable 5 <[email protected]>
Benchmark evidence: the with-skills image run probed pandas - a tabular-only dependency the image path never touches - and the raising import produced ModuleNotFoundError noise for the failure classifier. Step 4 now scopes dependencies to the detected modality (tabular needs pandas for DataFrames; images need numpy plus Pillow or the format loader, never pandas) and prescribes non-raising preflights (importlib.util.find_spec) so absent optional deps report cleanly instead of stack-tracing. The image reference states pandas is not an image-path dependency, and the image eval judges both behaviors. Co-Authored-By: Claude Fable 5 <[email protected]>
Skill-change review findings: - The intro still said 'point at tabular data' - the skill has carried the image path since v0.2; it now says tabular or image. - Step 5 was silently tabular-only (generate from df_stats_client.py) while steps 3-4 branch both modalities; it now names the image branch first so an image-path agent following the numbered workflow is never pointed at the wrong template. - Step 4's 'never pandas' for images conflicted with the image reference's own companion-labels follow-up (a tabular run that needs pandas); the rule is now scoped, not absolute. - Restored the meaning my earlier trim destroyed in step 3's reference-loading clause. Accepted duplications (min/max noised x3, fail-closed inputs x3, bin cap x4 docs) re-verified consistent; find_spec-vs-broken-install noted as covered by the simulator rung. Co-Authored-By: Claude Fable 5 <[email protected]>
Run 321039: the agent-authored parity checker guessed the stats JSON schema twice (a 'bins' key that does not exist; a scalar count treated as a nested object) before self-correcting. The references documented the hierarchy but never the leaf shapes - facts we verified in the original smoke test and failed to write down. The validation reference now states them (scalars are plain numbers; histogram leaves are [low, high, count] triples; quantiles are percentile->value mappings) plus the rule that closes the class: probe the actual output JSON before writing any checker and derive shapes from the file, never from guessed keys. Both parity evals judge the behavior. Co-Authored-By: Claude Fable 5 <[email protected]>
The manual-test datasets are offline assets, not part of this PR; the fixture README no longer references their local folder name. Co-Authored-By: Claude Fable 5 <[email protected]>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4890 +/- ##
==========================================
+ Coverage 60.77% 60.90% +0.13%
==========================================
Files 977 978 +1
Lines 93211 93539 +328
==========================================
+ Hits 56649 56974 +325
- Misses 36562 36565 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The eval image dataset is now created at eval time by generate_images.py (seeded, deterministic - same 110 PNGs per site plus the corrupt file), which the eval prompt explicitly asks the agent to run first. No binary image files remain in the repo; the fixture lint is satisfied by the script, and data generation stays a user-requested effect consistent with the skill's authorization rules. Co-Authored-By: Claude Fable 5 <[email protected]>
Contributor
Greptile SummaryThis PR adds federated statistics support for dataset-focused agent workflows. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (12): Last reviewed commit: "Document what recipe.execute returns so ..." | Re-trigger Greptile |
Greptile P2 on the PR: failure_images accumulated across histogram() calls, so a retried pass counted the same corrupt file twice, inflating failure_count (a pattern inherited from the official image_stats example). Failed paths are now a deduplicated set, making failure_count retry-safe. Co-Authored-By: Claude Fable 5 <[email protected]>
Skill .py files are loaded into LLM context at runtime, so every header line is a recurring per-invocation token cost. The 2-line SPDX header carries the same license terms as the 13-line boilerplate at about a third of the tokens. The license checker now requires the SPDX form under skills/ and the full boilerplate everywhere else, and skills/ is added to the checked folders (it was previously not scanned at all). Co-Authored-By: Claude Fable 5 <[email protected]>
Greptile P1 on the PR: failure_count is collected with the first-round statistics, but the template only discovered unreadable files during the round-2 histogram pass - so the server consumed 0 and the privacy cleansers treated corrupt files as readable. initialize() now runs a cheap header verification (PIL verify, no full decode; adaptation point notes pydicom's stop_before_pixels for DICOM) so failure_count is correct from the first collection. Verified against a simulator run: the Global failure_count row now reports 1 for the corrupt-file fixture where it previously reported 0 - this also neutralizes the user-visible effect of NVFlare issue NVIDIA#4876 for header-detectable corruption; the reference keeps the scoped caveat for decode-time failures only. Co-Authored-By: Claude Fable 5 <[email protected]>
Greptile P1 follow-up: Image.verify() is header-only, so a file with a valid header but broken pixel data passed round-1 verification and failed only during the round-2 histogram - after the server had consumed failure_count for count-minus-failures privacy decisions. initialize() now verifies each file by calling _load_image itself, so verification cannot disagree with the histogram's read path by construction; adapted clients that swap the loader inherit consistent verification for free. The trade-off (each image decodes twice) is documented with the relaxation note for very large datasets. Simulator re-verified: Global failure_count still 1 for the corrupt fixture. Co-Authored-By: Claude Fable 5 <[email protected]>
…ata reading The dependency step sat after data examination and only guarded "recipe construction or simulation", so an exploratory raising import (e.g. pandas on tabular data) could hit ModuleNotFoundError before the install ever ran — exactly the ordering error the conversion skills' wording was designed to prevent. Align fedstats with that ordering: install right after the static inspect step, guarded against any import-level preflight or exploratory data reading. Co-Authored-By: Claude Fable 5 <[email protected]>
Benchmark run 986631 (claude, tabular healthcare): the agent's parity checker followed this reference's exact-at-precision rule for Global values and failed on two of 23 features (stddev off by exactly 1 ulp: 0.4958 vs 0.4957, 1.6611 vs 1.6612), then correctly diagnosed the cause with a full-precision comparison and loosened its tolerance. The product rounds each site's second-round variance term to the persisted precision before the server sums them, so global stddev/var carry up to site-count ulps of double-rounding error versus a full-precision recompute. The reference was the bug: 'exact at persisted precision' is the per-site contract, not the Global one. Global tolerances are now defined (count exact; sum/mean 1 ulp; stddev/var site-count ulps) with the mechanism explained, so checkers stop false-failing. Co-Authored-By: Claude Fable 5 <[email protected]>
Greptile P1: a site where every image is unreadable raised out of histogram(), turning a representable data-quality condition (round-1 already reports count == failure_count) into a task exception that aborted the federated job for the healthy sites. The histogram now returns proper bin edges with zero counts for such a site; the report surfaces the condition through failure_count. Verified: an all-corrupt site yields 10 zero-count bins with failure_count=3 and no exception. Co-Authored-By: Claude Fable 5 <[email protected]>
Second benchmark occurrence of the same noise class (codex guessed nvflare.recipe.recipe; claude guessed nvflare.app_common.executors.statistics_task_handler): agents doing last-resort source reading invent module paths and generate exit-1 ImportError noise. The references-before-source rule now adds: locate modules by grepping the installed tree, never by guessing import paths. Co-Authored-By: Claude Fable 5 <[email protected]>
Run 913878's source dive answered a real gap: the references said 'numeric features only' and the template said 'return the DataFrame', but nothing said WHO drops the text columns. The agent went to the executor source to find out (the product filters to numeric dtypes client-side). Now stated explicitly in the mapping reference and the tabular template: return the full DataFrame, never drop columns in load_data; the skill's job is to know and name the exclusions. Co-Authored-By: Claude Fable 5 <[email protected]>
Per the product contract: fed stats accepts categorical input and filters non-numeric features automatically. The requirement bullet that read 'must compute statistics only for numeric features' could be taken as an instruction to filter up front; it now states the product does the filtering and the agent's duty is naming the exclusions and reporting missing rates. Co-Authored-By: Claude Fable 5 <[email protected]>
Even reworded as 'the product filters automatically', the bullet risks reading as an action item. Removed: the agent's only duties here are naming the non-numeric exclusions and reporting missing rates; the product-filters-automatically detail stays in the mapping reference and template where the mechanism belongs. Co-Authored-By: Claude Fable 5 <[email protected]>
Third source-dive of the same shape: the agent grepped correctly (found class Run in nvflare/recipe/run.py) but bundled discovery and import in one command and guessed 'from nvflare.recipe.spec import Run' from the sibling-class prior (Recipe lives in spec). The motive is ours to remove: the references said where output lands but never what execute() returns. Now stated: execute(env) returns a Run handle from nvflare.recipe.run (not spec), and validation does not need it - success is determined by the completeness/parity rungs over the output JSON. Co-Authored-By: Claude Fable 5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
nvflare-fed-stats— a data-first, fully automatic agent skill for federated statistics over tabular and image datasets — plus the product-side capability it consumes: dataset-target classification innvflare agent inspect.The skill (
skills/nvflare-fed-stats/)countis always included;var/stddevauto-expand theircount+sum+meansecond-round prerequisites.StatsJobwires min-count, min/max-noise, bin-cap cleansers per client); reports state the applied values as heuristic disclosure-risk reductions, never as privacy guarantees; aggregates only — no raw rows/cell values/pixels in any output.FedStatsRecipesimulator runs, not transcribed from docs: the feature-first output hierarchy, the precision-4 rounding, histogram leaf shapes ([low, high, count]triples), and the bin-cap boundary (20 bins first passes at 206 effective rows).nvflare agent inspect: dataset targets (nvflare/tool/agent/dataset_inspect.py)One inspection architecture for all skills: code targets keep the AST path; data-only directories now classify as
tabular_dataset/image_datasetwith a metadata-only dataset block — site layout, feature names (strong header rule: every numeric-bodied column non-numeric, names unique/non-empty — masked rows likeSUPPRESSED,100never leak as names), dtype classes, per-site row/file counts, cross-siteschema_agreement(names, column counts, dtypes, shard drift), parquet metadata via optional pyarrow, per-siteimage_formatsand sampledpixel_depth— and recommendnvflare-fed-stats. Walks are bounded (data-file budget + 50k entry cap, symlink-free), every data read is accounted in ascansub-block, and the full contract lives in the module docstring. Real code targets keep priority: only unknown targets or utility-only frameworks (numpy helpers) are overridden. The CLI gains--max-files/--max-file-bytes.Also included
failure_countmisses round-2 failures) — documented as a per-site diagnostic until fixed.Verification
failure_count).🤖 Generated with Claude Code