Overview
All five public autofit.plot functions declare **kwargs and never reference it — a caller's customization is accepted without error and has no effect. The failure is silent: no TypeError, no warning, just a plot that ignores what you asked for. autofit_workspace/scripts/plot/*.py teaches the broken idiom at four call sites (76 kwargs), and states in prose that the kwargs are forwarded; PyAutoFit/docs/cookbooks/search.md carries the same claim. Verifying the report surfaced a second silent defect in the same call: corner_cornerpy passes weight_list= to corner.corner, which has no such parameter (it is weights) — so sample weights are currently ignored on every weighted corner plot.
Fix direction chosen: forward the kwargs, with a strict signature filter. Unrecognised names raise TypeError rather than vanishing. Both defects are fixed in the same wave.
Plan
- Add a shared kwargs-forwarding helper to
plot_util.py that validates caller kwargs against the target function's named parameters and raises TypeError on anything unrecognised, with a did-you-mean hint.
- Forward validated kwargs from all five plot functions to their underlying library (
corner.corner, anesthetic, matplotlib), with library-computed values acting as defaults the caller can override.
- Fix the
weight_list= → weights= bug so weighted posteriors plot correctly.
- Preserve the
_corner_range_from degenerate-column guard: it applies whenever the caller omits range or passes None.
- Stop
@log_plot_exception swallowing the new validation TypeError on corner_anesthetic.
- Rewrite the four workspace scripts' kwarg lists to genuine
corner.py kwargs and correct the prose that claims forwarding — three of the four were copy-pasted from a different sampler's plotting API.
- Cover both defects with tests, and confirm test-mode/degenerate input still renders.
Detailed implementation plan
Work Classification
Both (library + workspace). The library PR merges first; the workspace PR follows behind the library-first merge gate.
Affected Repositories
PyAutoLabs/PyAutoFit (primary)
PyAutoLabs/autofit_workspace
Branch Survey
| Repository |
Current Branch |
Dirty? |
| PyAutoFit |
main |
clean |
| autofit_workspace |
main |
clean |
worktree_check_conflict autofit-plot-functions-kwargs PyAutoFit autofit_workspace → exit 0 (no conflict). autofit_workspace carries a stale unregistered branch feature/plot-api-update (HEAD 389e968, "Regenerate notebooks from updated scripts") — a warning, not a block.
Suggested branch: claude/autofit-plot-functions-kwargs-vvwj5x (both repos)
Worktree root: ~/Code/PyAutoLabs-wt/autofit-plot-functions-kwargs/ (created later by /start_library)
Grounding
Verified against PyAutoFit 055bb3d99 and autofit_workspace 94671f62, with corner 2.3.0 installed and exercised.
The **kwargs are dead. grep -n kwargs over the three plot modules matches only def lines.
weights are dropped. corner.corner's signature has weights, not weight_list. Unrecognised names fall into corner_impl(**hist2d_kwargs) → hist2d(**kwargs), whose body reads only extent and discards the rest. Confirmed empirically: corner.corner(data=..., weight_list=w, ...) returns a figure with no error and unweighted contours.
Forwarding alone would not fix the tutorials. Because of that same hist2d sink, wrong-library kwargs would stay silently ignored one layer deeper. Measured against corner 2.3.0's real signature:
| script |
kwargs |
take effect |
still dropped |
emcee_plotter.py |
30 |
30 |
0 |
dynesty_plotter.py |
19 |
14 |
5 — dims, span, quantiles_2d, hist2d_kwargs, truth_kwargs (dynesty's API) |
zeus_plotter.py |
16 |
6 |
10 — span, truth, alpha, linewidth, fill, fontsize, title_fontsize, cut, size, weight_list (zeus's API) |
nautilus_plotter.py |
11 |
8 |
3 — panelsize, yticksize, xticksize |
This is why the fix is forward + strict filter, not plain forwarding: the filter converts those 18 into loud errors, and the workspace rewrite removes them.
Collision hazards.
zeus_plotter.py passes weight_list=None, which duplicates the library's own weight_list= keyword → TypeError: got multiple values for keyword argument. A hard crash, not silence.
range collides twice: emcee_plotter.py passes range=None, nautilus_plotter.py passes range=np.ones(n) * 0.999. Naive caller-wins defeats _corner_range_from's degenerate-column guard.
corner_anesthetic is wrapped in @log_plot_exception, which catches TypeError — a validation error there would surface as a misleading "posterior estimate not yet sufficient" info log.
Implementation Steps — PyAutoFit (PR 1, merges first)
-
autofit/non_linear/plot/plot_util.py — add the forwarding helper:
def forward_kwargs(kwargs, *, accepts, reserved=(), target=""):
accepts is the set of parameter names the target genuinely honours; a target's own **kwargs is deliberately not treated as "accepts anything" (corner's hist2d_kwargs sink is exactly the trap being closed). Unrecognised names raise a PlotKwargsError(TypeError) naming them, with difflib.get_close_matches supplying a hint (weight_list → "did you mean weights?"). reserved names arguments the library owns and the caller may never override.
For corner, accepts = named parameters of corner.corner ∪ named parameters of corner.core.hist2d (minus x, y) — hist2d kwargs are a documented, legitimate pass-through, so they stay valid.
-
samples_plotters.py::corner_cornerpy
weight_list= → weights=.
- Build
defaults = {"weights": ..., "labels": ..., "range": _corner_range_from(data)}, validate the caller's kwargs, then defaults.update(validated). data is reserved.
range: the guard is the default, so it applies when the caller omits range or passes None; a non-None caller value wins.
- Keep the existing
data.ndim < 2 or data.shape[0] <= data.shape[1] early return.
-
nest_plotters.py::corner_anesthetic — route validated kwargs to their actual targets (make_2d_axes vs NestedSamples.plot_2d); anything matching neither raises. Narrow @log_plot_exception to re-raise PlotKwargsError so validation failures are not reported as an unconverged posterior.
-
mle_plotters.py — subplot_parameters, log_likelihood_vs_iteration, figure_of_merit_vs_iteration: forward validated kwargs to plt.plot / plt.semilogy (Line2D properties), reserving c.
-
Internal callers — abstract_mcmc.py:56, abstract_nest.py:77, abstract_mle.py:38-64 pass only samples/path/format/the explicit flags, so no call-site changes; confirm none regress.
-
docs/cookbooks/search.md:165 — the 30-kwarg emcee list stays valid (all 30 are genuine corner kwargs); update the surrounding prose to state that kwargs are forwarded and validated.
-
Tests — test_autofit/non_linear/plot/test_samples_plotters.py (+ new modules for nest/mle):
- a recognised kwarg reaches the target (monkeypatch
corner.corner, assert receipt);
- an unrecognised kwarg raises
TypeError naming it;
weights is passed through as weights= and equals samples.weight_list;
- caller
range=None still yields the _corner_range_from guard; a non-None range wins;
- degenerate-column input (every sample equal) still renders without corner's "no dynamic range" crash;
corner_anesthetic validation error is not swallowed by @log_plot_exception.
Implementation Steps — autofit_workspace (PR 2, behind the library-first gate)
- Rewrite the four
scripts/plot/*.py kwarg lists to genuine corner.py kwargs only — drop dynesty's 5, zeus's 10, nautilus's 3. zeus_plotter.py's weight_list=None must go.
- Correct the prose in each script so the claim matches behaviour (kwargs are forwarded to
corner.py and validated).
- Regenerate the notebooks from the scripts per workspace convention.
Verify
- A call passing a non-default kwarg (e.g.
bins=5) visibly changes the output figure.
- A bogus kwarg raises
TypeError naming it, rather than being ignored.
- Weighted samples produce a visibly different corner figure than the same samples unweighted.
- Degenerate-column input (e.g. a
PYAUTO_TEST_MODE=1 run) still renders — the _corner_range_from guard survives.
- No workspace script still passes an argument that does nothing.
Key Files
autofit/non_linear/plot/plot_util.py — new forward_kwargs helper + PlotKwargsError; log_plot_exception narrowed
autofit/non_linear/plot/samples_plotters.py — corner_cornerpy: forwarding + the weights fix
autofit/non_linear/plot/nest_plotters.py — corner_anesthetic: split forwarding
autofit/non_linear/plot/mle_plotters.py — the three matplotlib trace plots
docs/cookbooks/search.md — prose claim about kwargs
test_autofit/non_linear/plot/test_samples_plotters.py — extended coverage
autofit_workspace/scripts/plot/{emcee,dynesty,zeus,nautilus}_plotter.py — kwarg lists + prose
Sizing note
pyauto-brain bug and the sizing faculty both return too-large (score 13) and recommend phasing. Recorded and overridden: the score is inflated by prompt word-count plus the multi-repo flag. The actual change is ~15 lines of library code, four workspace scripts, and one docs snippet — the prompt's own Difficulty: small header is the better estimate. Not phased; one library PR plus its paired workspace PR.
Original Prompt
Click to expand starting prompt
autofit.plot functions accept **kwargs and silently discard them
Type: bug
Target: autofit
Repos:
- PyAutoFit
- autofit_workspace
Difficulty: small
Autonomy: supervised
Priority: normal
Status: formalised
Filed: 2026-08-07 (backfilled from git)
Filed 2026-08-07, found while fixing docs/api/plot.rst
(complete/2026/08/pyautofit_plot_rst_dead_plotters.md). Deliberately left out of
that PR: it is a library/workspace defect, not a docs one, and the docs change
was docs-only by design.
The defect
All five public autofit.plot functions take **kwargs in their signature and
never reference it in the body. Verified mechanically — kwargs appears in
autofit/non_linear/plot/{samples_plotters,nest_plotters,mle_plotters}.py only
on the def lines:
corner_cornerpy(samples, path=None, filename="corner", format="show", **kwargs)
corner_anesthetic(samples, path=None, filename="corner_anesthetic", format="show", **kwargs)
subplot_parameters(...), log_likelihood_vs_iteration(...),
figure_of_merit_vs_iteration(...) — same shape
corner_cornerpy calls the underlying library with a fixed argument set:
corner.corner(
data=data,
weight_list=samples.weight_list,
labels=samples.model.parameter_labels_with_superscripts_latex,
range=_corner_range_from(data),
)
So a caller's customization is accepted without error and has no effect. The
failure mode is the bad one: silent. No TypeError, no warning, just a plot
that ignores what you asked for.
Why it matters — the workspace teaches the broken idiom
autofit_workspace/scripts/plot/*.py passes long kwarg lists as though they
were forwarded, and says so in prose: "In all the examples below, we use the
kwargs of this function to pass in any of the input parameters that are
described in the API docs." That claim is false today.
Silently-discarded kwargs at those call sites (counted 2026-08-07):
| script |
discarded kwargs |
scripts/plot/emcee_plotter.py |
30 |
scripts/plot/dynesty_plotter.py |
19 |
scripts/plot/zeus_plotter.py |
16 |
scripts/plot/nautilus_plotter.py |
11 |
| total |
76 |
A user following the plot tutorials sets bins, smooth, show_titles,
truths, … and sees none of them applied. The tutorials are the documentation
for this API, so this is the primary way the behaviour is encountered.
The decision to make (why supervised, not auto)
Two coherent fixes; picking one is a judgement about the intended surface:
- Forward them — pass
**kwargs through to corner.corner /
anesthetic / matplotlib. Matches what the workspace already claims and
makes the existing tutorials correct as written. Watch the collisions:
corner_cornerpy already sets data, weight_list, labels and range
explicitly, and range in particular is computed by _corner_range_from
to dodge corner's "no dynamic range" crash on degenerate columns — a
user-supplied range must not silently reintroduce that. Decide precedence
(caller wins / library wins) and state it.
- Drop
**kwargs from the signatures and correct the workspace scripts +
prose. Honest, and callers get a loud TypeError instead of silence — but
it removes customization the tutorials imply exists, so it is the bigger
user-facing change.
Either way the workspace scripts and their prose need updating in the same
wave, so this is a paired PyAutoFit + autofit_workspace task.
Verify
- A call passing a non-default kwarg (e.g.
bins=5) visibly changes the
output figure (fix 1), or raises TypeError (fix 2).
- The four
scripts/plot/*.py tutorials and their surrounding prose agree with
whichever behaviour was chosen — no script still passes an argument that does
nothing.
- Degenerate-column input (every sample equal, e.g. a
PYAUTO_TEST_MODE=1 run)
still does not crash corner, i.e. the _corner_range_from guard survives.
Overview
All five public
autofit.plotfunctions declare**kwargsand never reference it — a caller's customization is accepted without error and has no effect. The failure is silent: noTypeError, no warning, just a plot that ignores what you asked for.autofit_workspace/scripts/plot/*.pyteaches the broken idiom at four call sites (76 kwargs), and states in prose that the kwargs are forwarded;PyAutoFit/docs/cookbooks/search.mdcarries the same claim. Verifying the report surfaced a second silent defect in the same call:corner_cornerpypassesweight_list=tocorner.corner, which has no such parameter (it isweights) — so sample weights are currently ignored on every weighted corner plot.Fix direction chosen: forward the kwargs, with a strict signature filter. Unrecognised names raise
TypeErrorrather than vanishing. Both defects are fixed in the same wave.Plan
plot_util.pythat validates caller kwargs against the target function's named parameters and raisesTypeErroron anything unrecognised, with a did-you-mean hint.corner.corner,anesthetic,matplotlib), with library-computed values acting as defaults the caller can override.weight_list=→weights=bug so weighted posteriors plot correctly._corner_range_fromdegenerate-column guard: it applies whenever the caller omitsrangeor passesNone.@log_plot_exceptionswallowing the new validationTypeErroroncorner_anesthetic.corner.pykwargs and correct the prose that claims forwarding — three of the four were copy-pasted from a different sampler's plotting API.Detailed implementation plan
Work Classification
Both (library + workspace). The library PR merges first; the workspace PR follows behind the library-first merge gate.
Affected Repositories
PyAutoLabs/PyAutoFit(primary)PyAutoLabs/autofit_workspaceBranch Survey
mainmainworktree_check_conflict autofit-plot-functions-kwargs PyAutoFit autofit_workspace→ exit 0 (no conflict).autofit_workspacecarries a stale unregistered branchfeature/plot-api-update(HEAD389e968, "Regenerate notebooks from updated scripts") — a warning, not a block.Suggested branch:
claude/autofit-plot-functions-kwargs-vvwj5x(both repos)Worktree root:
~/Code/PyAutoLabs-wt/autofit-plot-functions-kwargs/(created later by/start_library)Grounding
Verified against PyAutoFit
055bb3d99and autofit_workspace94671f62, withcorner2.3.0 installed and exercised.The
**kwargsare dead.grep -n kwargsover the three plot modules matches onlydeflines.weightsare dropped.corner.corner's signature hasweights, notweight_list. Unrecognised names fall intocorner_impl(**hist2d_kwargs)→hist2d(**kwargs), whose body reads onlyextentand discards the rest. Confirmed empirically:corner.corner(data=..., weight_list=w, ...)returns a figure with no error and unweighted contours.Forwarding alone would not fix the tutorials. Because of that same
hist2dsink, wrong-library kwargs would stay silently ignored one layer deeper. Measured against corner 2.3.0's real signature:emcee_plotter.pydynesty_plotter.pydims,span,quantiles_2d,hist2d_kwargs,truth_kwargs(dynesty's API)zeus_plotter.pyspan,truth,alpha,linewidth,fill,fontsize,title_fontsize,cut,size,weight_list(zeus's API)nautilus_plotter.pypanelsize,yticksize,xticksizeThis is why the fix is forward + strict filter, not plain forwarding: the filter converts those 18 into loud errors, and the workspace rewrite removes them.
Collision hazards.
zeus_plotter.pypassesweight_list=None, which duplicates the library's ownweight_list=keyword →TypeError: got multiple values for keyword argument. A hard crash, not silence.rangecollides twice:emcee_plotter.pypassesrange=None,nautilus_plotter.pypassesrange=np.ones(n) * 0.999. Naive caller-wins defeats_corner_range_from's degenerate-column guard.corner_anestheticis wrapped in@log_plot_exception, which catchesTypeError— a validation error there would surface as a misleading "posterior estimate not yet sufficient" info log.Implementation Steps — PyAutoFit (PR 1, merges first)
autofit/non_linear/plot/plot_util.py— add the forwarding helper:acceptsis the set of parameter names the target genuinely honours; a target's own**kwargsis deliberately not treated as "accepts anything" (corner'shist2d_kwargssink is exactly the trap being closed). Unrecognised names raise aPlotKwargsError(TypeError)naming them, withdifflib.get_close_matchessupplying a hint (weight_list→ "did you meanweights?").reservednames arguments the library owns and the caller may never override.For corner,
accepts= named parameters ofcorner.corner∪ named parameters ofcorner.core.hist2d(minusx,y) — hist2d kwargs are a documented, legitimate pass-through, so they stay valid.samples_plotters.py::corner_cornerpyweight_list=→weights=.defaults = {"weights": ..., "labels": ..., "range": _corner_range_from(data)}, validate the caller's kwargs, thendefaults.update(validated).dataisreserved.range: the guard is the default, so it applies when the caller omitsrangeor passesNone; a non-Nonecaller value wins.data.ndim < 2 or data.shape[0] <= data.shape[1]early return.nest_plotters.py::corner_anesthetic— route validated kwargs to their actual targets (make_2d_axesvsNestedSamples.plot_2d); anything matching neither raises. Narrow@log_plot_exceptionto re-raisePlotKwargsErrorso validation failures are not reported as an unconverged posterior.mle_plotters.py—subplot_parameters,log_likelihood_vs_iteration,figure_of_merit_vs_iteration: forward validated kwargs toplt.plot/plt.semilogy(Line2Dproperties), reservingc.Internal callers —
abstract_mcmc.py:56,abstract_nest.py:77,abstract_mle.py:38-64pass onlysamples/path/format/the explicit flags, so no call-site changes; confirm none regress.docs/cookbooks/search.md:165— the 30-kwarg emcee list stays valid (all 30 are genuine corner kwargs); update the surrounding prose to state that kwargs are forwarded and validated.Tests —
test_autofit/non_linear/plot/test_samples_plotters.py(+ new modules for nest/mle):corner.corner, assert receipt);TypeErrornaming it;weightsis passed through asweights=and equalssamples.weight_list;range=Nonestill yields the_corner_range_fromguard; a non-Nonerangewins;corner_anestheticvalidation error is not swallowed by@log_plot_exception.Implementation Steps — autofit_workspace (PR 2, behind the library-first gate)
scripts/plot/*.pykwarg lists to genuinecorner.pykwargs only — drop dynesty's 5, zeus's 10, nautilus's 3.zeus_plotter.py'sweight_list=Nonemust go.corner.pyand validated).Verify
bins=5) visibly changes the output figure.TypeErrornaming it, rather than being ignored.PYAUTO_TEST_MODE=1run) still renders — the_corner_range_fromguard survives.Key Files
autofit/non_linear/plot/plot_util.py— newforward_kwargshelper +PlotKwargsError;log_plot_exceptionnarrowedautofit/non_linear/plot/samples_plotters.py—corner_cornerpy: forwarding + theweightsfixautofit/non_linear/plot/nest_plotters.py—corner_anesthetic: split forwardingautofit/non_linear/plot/mle_plotters.py— the three matplotlib trace plotsdocs/cookbooks/search.md— prose claim about kwargstest_autofit/non_linear/plot/test_samples_plotters.py— extended coverageautofit_workspace/scripts/plot/{emcee,dynesty,zeus,nautilus}_plotter.py— kwarg lists + proseSizing note
pyauto-brain bugand the sizing faculty both return too-large (score 13) and recommend phasing. Recorded and overridden: the score is inflated by prompt word-count plus the multi-repo flag. The actual change is ~15 lines of library code, four workspace scripts, and one docs snippet — the prompt's ownDifficulty: smallheader is the better estimate. Not phased; one library PR plus its paired workspace PR.Original Prompt
Click to expand starting prompt
autofit.plotfunctions accept**kwargsand silently discard themType: bug
Target: autofit
Repos:
Difficulty: small
Autonomy: supervised
Priority: normal
Status: formalised
Filed: 2026-08-07 (backfilled from git)
Filed 2026-08-07, found while fixing
docs/api/plot.rst(complete/2026/08/pyautofit_plot_rst_dead_plotters.md). Deliberately left out of
that PR: it is a library/workspace defect, not a docs one, and the docs change
was docs-only by design.
The defect
All five public
autofit.plotfunctions take**kwargsin their signature andnever reference it in the body. Verified mechanically —
kwargsappears inautofit/non_linear/plot/{samples_plotters,nest_plotters,mle_plotters}.pyonlyon the
deflines:corner_cornerpy(samples, path=None, filename="corner", format="show", **kwargs)corner_anesthetic(samples, path=None, filename="corner_anesthetic", format="show", **kwargs)subplot_parameters(...),log_likelihood_vs_iteration(...),figure_of_merit_vs_iteration(...)— same shapecorner_cornerpycalls the underlying library with a fixed argument set:So a caller's customization is accepted without error and has no effect. The
failure mode is the bad one: silent. No
TypeError, no warning, just a plotthat ignores what you asked for.
Why it matters — the workspace teaches the broken idiom
autofit_workspace/scripts/plot/*.pypasses long kwarg lists as though theywere forwarded, and says so in prose: "In all the examples below, we use the
kwargsof this function to pass in any of the input parameters that aredescribed in the API docs." That claim is false today.
Silently-discarded kwargs at those call sites (counted 2026-08-07):
scripts/plot/emcee_plotter.pyscripts/plot/dynesty_plotter.pyscripts/plot/zeus_plotter.pyscripts/plot/nautilus_plotter.pyA user following the plot tutorials sets
bins,smooth,show_titles,truths, … and sees none of them applied. The tutorials are the documentationfor this API, so this is the primary way the behaviour is encountered.
The decision to make (why supervised, not auto)
Two coherent fixes; picking one is a judgement about the intended surface:
**kwargsthrough tocorner.corner/anesthetic/ matplotlib. Matches what the workspace already claims andmakes the existing tutorials correct as written. Watch the collisions:
corner_cornerpyalready setsdata,weight_list,labelsandrangeexplicitly, and
rangein particular is computed by_corner_range_fromto dodge corner's "no dynamic range" crash on degenerate columns — a
user-supplied
rangemust not silently reintroduce that. Decide precedence(caller wins / library wins) and state it.
**kwargsfrom the signatures and correct the workspace scripts +prose. Honest, and callers get a loud
TypeErrorinstead of silence — butit removes customization the tutorials imply exists, so it is the bigger
user-facing change.
Either way the workspace scripts and their prose need updating in the same
wave, so this is a paired PyAutoFit + autofit_workspace task.
Verify
bins=5) visibly changes theoutput figure (fix 1), or raises
TypeError(fix 2).scripts/plot/*.pytutorials and their surrounding prose agree withwhichever behaviour was chosen — no script still passes an argument that does
nothing.
PYAUTO_TEST_MODE=1run)still does not crash
corner, i.e. the_corner_range_fromguard survives.