Skip to content

[OPIK-8411] MPM presence in growth-report via --mpm (OPIK-8411) - #28

Merged
Nimrod007 merged 9 commits into
mainfrom
dsblank/opik-8411-mpm-growth-report
Oct 1, 2026
Merged

Nimrod007 merged 9 commits into
mainfrom
dsblank/opik-8411-mpm-growth-report

Conversation

@dsblank

@dsblank dsblank commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Adds MPM adoption to cometx admin growth-report for OPIK-8411: which workspaces use MPM and how many models each one monitors, next to the existing EM and Opik stats. It also fixes the admin API URL and the chargeback error messages, which were blocking growth-report on servers such as mpm-demo.

cometx admin growth-report --mpm --csv-dir ./out

How --mpm collects the data

The chargeback report has no MPM data, and the backend is out of scope, so --mpm gathers it from existing endpoints. It merges mpmEnabled / monitoredModels into each chargeback workspace before the report is built:

  1. /api/mpm/v3/workspaces: one call covering every workspace the API key's user belongs to. The path is deliberately fixed: it's a backend-react route, so it resolves the same way on Cloud and on chart deployments.
  2. REST v2 model registry for the other workspaces: registry-model?workspaceName= lists the models, then registry-model/details gives each model's monitored flag. This is one request per model, so the calls run in parallel (8 workers).

Both sources apply the same filter server-side: is_monitored, not pipeline-generated, no ref_production_model_id, not deleted (RegistryQueries.getMonitoredRegistryModelsForWorkspacesSql). When workspaces are named on the command line, only those are checked. --mpm is off by default because of the per-model requests.

Nothing unknown is reported as zero or as complete:

  • A workspace whose lookups fail, or whose flag can't be read, is unknown, not zero. A failed lookup also removes any MPM fields already in the input, so stale values aren't passed through.
  • Partial totals are labeled. The MPM-workspaces percentage is over the workspaces actually checked ("100% of 1 checked; 1 unknown"). A model total missing any workspace is shown as a lower bound ("≥ N", "MPM checked in K/M workspaces").
  • A failed mpm/v3/workspaces call is classified, not swallowed (review feedback). The results are refused (401/403), not_found (404: MPM not installed or not routed) and error. Each prints a warning, is shown in the HTML notes, and is recorded in the CSV. refused also warns that counts may be low (see Access below).
  • nb_models_registered is not used, because it counts every registry model (the ticket's caveat).

Output

  • HTML report
    • Organization overview: MPM workspaces and Monitored models cards, and an MPM models column in the by-workspace table.
    • Leaderboards: top/bottom workspaces by MPM monitored models.
    • The platform-mix chart is unchanged. Its hint says whether MPM was collected, and how.
  • growth_workspaces.csv: new mpm_enabled (1/0, so SUM() counts MPM workspaces) and num_monitored_models columns, appended at the end so existing columns keep their order. Both are empty when that workspace's MPM status is unknown.
  • growth_org_kpis.csv:
    • mpm_workspaces and total_monitored_models: exact only when mpm_workspaces_unchecked is 0.
    • mpm_workspaces_unchecked: workspaces whose model count is unknown. Emitted even when every lookup fails.
    • mpm_member_lookup: a label metric (ok / refused / not_found / error), so a dashboard can tell "MPM absent" from "auth refused".

Access: who counts as an admin

The two parts of this report use different permission checks:

  • Chargeback (@AdminApiKey, ApiKeyAuthFilter): the key's user must be in the server's admin user list or, on self-hosted installs, an organization admin. Workspace roles such as Manage don't count.
  • --mpm registry route: organization admins can read every workspace. For a workspace the user isn't a member of, a user who isn't an organization admin gets only its public models, with no error. Private monitored models are then missed. This is documented in --help, both READMEs, and the module docstring. mpm_member_lookup=refused flags the most likely case, but the code can't detect it directly.

Also fixed

  • Admin URLs under the SDK's /clientlib root. admin_api_url kept the /clientlib segment that the SDK's comet.url_override ends with. So chargeback, service-accounts, migrate-users and the MPM call all went to /clientlib/api/..., which is a 404 on mpm-demo. It now drops a trailing /clientlib segment and keeps any deployment prefix in front of it (/comet/clientlib becomes /comet), matching smoke_test.

  • Accurate chargeback errors. Every chargeback failure used to print "requires an admin API key", including that 404. The message now depends on the failure:

    Failure Message
    401 / 403 requires an admin user's API key: server admin, or organization admin on self-hosted; workspace roles such as Manage don't count
    404 could not find the chargeback endpoint at <url>; check the server URL (not an API-key problem)
    non-JSON response likely an SSO or proxy login page
    anything else could not fetch the chargeback report, without blaming the key

    The detail shown is HTTP <status>: <server message>. Before, it was the SDK exception text, which included the first 5 characters of the API key and a truncated URL.

Limitations

  • Current state only. The monitored flag says what's monitored now, so past months can't be reconstructed. A trend builds up from monthly --csv-dir exports.
  • Prediction volume is out of scope per the ticket (it would need Druid).

Testing

  • 340 unit tests pass, and pre-commit hooks are clean on the changed files. tests/unit/test_admin_growth_mpm.py covers:

    • member-workspace coverage and the registry fallback
    • listing and detail failures, and a missing flag
    • both flag spellings
    • scoping to named workspaces
    • stale fields on a failed lookup
    • partial-coverage labels
    • member-lookup results (401, 403, 404, 502, network, unexpected response)
    • --mpm on and off through GrowthReporter.build

    Other test files cover the URL handling and each chargeback error message.

  • Live on mpm-demo.dev.comet.com (comet.mpm.enabled), full run with an admin key (growth-report --mpm --csv-dir, no URL override): exit 0 in about 4s.

    • 10 workspaces: 1 answered by mpm/v3/workspaces, 9 via the registry, mpm_member_lookup=ok, mpm_workspaces_unchecked=0.
    • 4 MPM workspaces (40%) with 18 monitored models, shown as complete in the cards and the CSV.
    • The registry returned private models in 3 workspaces the key's user isn't a member of, so nothing was missed.
    • The details response spells the flag monitored, not isMonitored. Both are accepted.
    • Before the admin role was granted: the non-admin key got the 401 admin-key message, where it previously got a 404 from the /clientlib URL.
  • Not tested live: the refused and not_found lookup results (unit tests only), and a SageMaker deployment. Review feedback traced the SageMaker auth path through api._client and found no code needed.

🤖 Generated with Claude Code

Chargeback carries no MPM data, so `--mpm` collects it client-side from
existing endpoints and merges it into each chargeback workspace:

- /api/mpm/v3/workspaces answers, in one call, every workspace the API
  key's user belongs to.
- The REST v2 model registry covers the rest: list each workspace's
  models, then read each model's monitored flag (parallel, 8 workers).

Both apply the same is_monitored predicate server-side. A workspace whose
lookup fails is reported as unknown, never as zero.

Adds MPM workspaces / Monitored models KPIs, an MPM models column in the
by-workspace table, an MPM leaderboard, mpm_enabled / num_monitored_models
columns in growth_workspaces.csv, and mpm_workspaces /
total_monitored_models / mpm_workspaces_unchecked org KPIs. Off by
default because of the per-model requests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@baz-reviewer

baz-reviewer Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review this PR on Baz

Baz Summary

Add optional --mpm collection to cometx admin growth-report, combining MPM workspace responses and registry model details with chargeback data to report monitored-model adoption, partial coverage, CSV metrics, HTML KPIs, and leaderboards. Correct admin URL construction and chargeback diagnostics, while applying formatting cleanup across CLI, framework, example, and reporting files.

graph LR
admin_("admin"):::modified
generate_growth_report_("generate_growth_report"):::modified
GrowthReporter_build_("GrowthReporter.build"):::modified
GrowthReporter_add_mpm_presence_("GrowthReporter._add_mpm_presence"):::added
fetch_mpm_presence_("fetch_mpm_presence"):::added
MPM_API_("MPM_API"):::added
registry_monitored_models_("_registry_monitored_models"):::added
apply_mpm_presence_("apply_mpm_presence"):::added
admin_ -- "Forwards --mpm to enable monitored-model collection in reports." --> generate_growth_report_
generate_growth_report_ -- "Passes MPM configuration so build conditionally enriches chargeback workspaces." --> GrowthReporter_build_
GrowthReporter_build_ -- "Collects selected workspace MPM data before assembling report output." --> GrowthReporter_add_mpm_presence_
GrowthReporter_add_mpm_presence_ -- "Requests monitored model lists and returns coverage or lookup status." --> fetch_mpm_presence_
fetch_mpm_presence_ -- "Sends API-key authorization and receives workspace monitored-model records." --> MPM_API_
fetch_mpm_presence_ -- "Falls back to registry listings and per-model monitored flags." --> registry_monitored_models_
GrowthReporter_add_mpm_presence_ -- "Adds mpmEnabled and monitoredModels fields, preserving unknown workspaces." --> apply_mpm_presence_
classDef added stroke:#15AA7A
classDef removed stroke:#CD5270
classDef modified stroke:#EDAC4C
linkStyle default stroke:#CBD5E1,font-size:13px
Loading

Topics

TopicDetails
MPM growth reporting Collect MPM presence through fetch_mpm_presence, merge monitored models into chargeback workspace records, preserve unknown and partial results, and expose the data through report KPIs, tables, leaderboards, CSV exports, CLI help, documentation, and regression tests.
Modified files (11)
  • README-ADMIN.md
  • README.md
  • cometx/cli/admin.py
  • cometx/cli/admin_growth_csv.py
  • cometx/cli/admin_growth_mpm.py
  • cometx/cli/admin_growth_report.py
  • cometx/cli/admin_growth_users.py
  • tests/unit/test_admin_growth_csv.py
  • tests/unit/test_admin_growth_mpm.py
  • tests/unit/test_admin_growth_report.py
  • tests/unit/test_admin_growth_users.py
Latest Contributors(2)
UserCommitDate
nimrod@comet.comfix(admin): split the ...October 01, 2026
doug@comet.comfix(admin): classify a...September 30, 2026
Admin API reliability Correct admin_api_url handling for SDK /clientlib roots and distinguish authentication failures, missing endpoints, malformed responses, and server errors so chargeback and MPM workflows produce accurate, safe diagnostics across deployment prefixes.
Modified files (5)
  • cometx/cli/admin_growth_report.py
  • cometx/utils.py
  • tests/unit/test_admin_growth_report.py
  • tests/unit/test_utils.py
  • tests/unit/test_utils_chargeback.py
Latest Contributors(2)
UserCommitDate
nimrod@comet.comfix(admin): split the ...October 01, 2026
doug@comet.comfix(admin): classify a...September 30, 2026
Codebase formatting Apply formatting and import-order cleanup to administrative reports, copy and download utilities, duplicate renaming, and example workflows without changing their behavior, improving consistency and maintainability across the broader CLI and sample surface.
Modified files (12)
  • cometx/cli/admin_gpu_app.py
  • cometx/cli/admin_gpu_report.py
  • cometx/cli/copy.py
  • cometx/cli/rename_duplicates.py
  • cometx/framework/comet/download_manager.py
  • examples/00_seed_demo_project.py
  • examples/01_inspect_views.py
  • examples/02_build_project_dashboard.py
  • examples/03_build_experiment_view.py
  • examples/04_migrate_views.py
  • examples/05_standardize_workspace.py
  • examples/06_harvest_panels.py
Latest Contributors(2)
UserCommitDate
nimrod@comet.comfix(admin): take an HT...October 01, 2026
chasefortierAdd workspace creation...March 30, 2026

Merger  Activate to get a short verdict whether this PR is good to go or not

Skills  Activate Skill Maintainer to keep your skills up to date

Planner  This PR would have been improved with Baz Planner - Try it now

Comment thread cometx/cli/admin_growth_report.py
Comment thread cometx/cli/admin_growth_mpm.py
Comment thread cometx/cli/admin_growth_users.py Outdated
Comment thread cometx/cli/admin_growth_users.py
Comment thread README-ADMIN.md Outdated
The SDK's comet.url_override ends in /clientlib/, and MPM is served next to
it. Under it, the call 404'd and --mpm silently fell back to the slower
per-model registry path. Found running against mpm-demo.dev.comet.com,
where /clientlib/api/mpm/v3/workspaces is 404 and /api/mpm/v3/workspaces
is 200.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment thread cometx/cli/admin_growth_mpm.py Outdated
Douglas Blank and others added 4 commits September 29, 2026 11:40
For a workspace the user isn't a member of, the registry returns only public
models to a user who isn't an organization admin, with no error. Private
monitored models are then missed silently and the workspace still counts as
checked. This rule differs from chargeback's (server admin list, or org
admin on-prem), so a key that passes chargeback may still undercount.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every chargeback failure used to be reported as "requires an admin API
key", including a 404 from a wrong URL or path prefix. Now:

- 401/403: needs an admin user's key (a server admin, or an organization
  admin on self-hosted); workspace roles such as Manage don't count.
- 404: endpoint not found at <url>; check the server URL.
- non-JSON response: likely an SSO or proxy login page.
- other failures: could not fetch, without blaming the key.

Show "HTTP <status>: <server message>" instead of the SDK exception text,
which included part of the API key and a truncated URL. Document who counts
as an admin in both READMEs.

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

admin_api_url kept every path prefix of the base URL, including the
/clientlib segment the SDK's comet.url_override ends with. That segment is
the SDK's own API root, and the admin and MPM APIs are served beside it, so
chargeback, service-accounts and migrate-users called
/clientlib/api/admin/... and got a 404 on servers such as mpm-demo, where
/api/admin/... answers. Drop a trailing /clientlib segment and keep any
real deployment prefix in front of it (/comet/clientlib -> /comet), the way
smoke_test already does.

The MPM-only workaround in admin_growth_mpm now just uses the shared helper.
The old test that asserted the /clientlib/api/admin URL is replaced by
cases for the SDK base, a deployment prefix, a prefix-only base, and a
lookalike segment.

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

Addresses the review comments on #28:

- MPM workspaces card: the percentage is over the workspaces actually
  checked ("100% of 1 checked; 1 unknown"), not over all workspaces.
- Monitored models: a total missing any workspace is shown as a lower
  bound ("≥ N"). workspace_org_totals now returns mpm_checked and
  mpm_unchecked, derived from the records, so partial coverage is labeled
  the same way for --chargeback-report files. The separate _mpm_status run
  state is removed.
- mpm_workspaces_unchecked counts workspaces with an unknown model count,
  and is emitted even when every lookup failed.
- apply_mpm_presence removes existing mpmEnabled / monitoredModels from a
  workspace whose lookup failed, instead of passing stale values through.
- A fractional or negative monitoredModels count is treated as unknown
  instead of being truncated.
- README-ADMIN: fix a typo, and say the CSV totals are exact only when
  mpm_workspaces_unchecked is 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dsblank dsblank changed the title feat(admin): MPM presence in growth-report via --mpm (OPIK-8411) [OPIK-8411] MPM presence in growth-report via --mpm (OPIK-8411) Sep 30, 2026
baz-reviewer[bot]
baz-reviewer Bot previously approved these changes Sep 30, 2026
@Nimrod007

Copy link
Copy Markdown
Contributor

SageMaker: the auth works and needs nothing here, but a failure would be invisible

Checked whether --mpm works under SageMaker Partner App auth, the way the MPM SDK does. It does, and admin_growth_mpm.py needs no SageMaker-specific code — because it calls through api._client rather than raw requests.

The chain: api._client.get(...) → BaseApiClient.get → LowLevelHTTPClient.get → session.get(...), where the session comes from comet_ml's get_comet_http_session(), which sets session.headers["Authorization"] = api_key and then calls setup_http_session_authentication(session). That checks AWS_PARTNER_APP_AUTH and installs session.auth = PartnerAppAuthProvider().get_auth() — the same provider the MPM SDK wires up in comet_mpm/authhook/aws_sagemaker.py. comet_ml carries its own copy at comet_ml/authhook/aws_sagemaker.py.

The explicit headers={"Authorization": api.api_key} is redundant, since the session already sets it, but harmless: requests runs prepare_auth() after prepare_headers(), so the signer still sees the Comet key and relocates it to X-Amz-Partner-App-Authorization with SigV4 in Authorization.

The hardcoded path is correct, and that is worth recording because it reads as a bug. /api/mpm/v3/workspaces looks like an MPM-service route, but @Path("mpm/v3/workspaces") is registered in ReactWebappServerApplication — it is a backend-react route, not an MPM-webapp one. So it resolves through the gateway on Cloud and through nginx's location /api/ on a chart deployment, and making it configurable would break it. Same shape as RECENT_EVENTS_ENDPOINT in the MPM SDK; the MPM query API (api/mpm/v2/...) is the one that genuinely varies by deployment, and this is not that.

Server side, MpmAuthFilter.extractApiKeyFromHeader reads HttpHeaders.AUTHORIZATION and expects a Comet API key or workspace JWT. Nothing in comet-backend handles X-Amz-Partner-App-Authorization, so restoring that header is the AWS proxy's job — which it must already do, since every comet_ml API call under SageMaker depends on the identical round-trip.

One thing I could not verify: the sagemaker package was not available in my environment, so I did not read PartnerAppAuthProvider.get_auth() itself. The relocation behaviour is inferred from the MPM SDK's comment on the sibling get_signed_request, plus the fact that get_comet_http_session sets the Authorization header before installing the signer, which only works if the signer moves it.


The one change I would ask for

_fetch_member_workspaces catches bare Exception and returns None, and the module docstring reads that as "MPM disabled on the deployment, network error, unexpected shape". Under SageMaker it also covers a 401, a 403, and a proxy routing miss — and all of them fall through to the registry path rather than surfacing.

That matters because of the second caveat this file already documents: on a non-member workspace, a caller who is not an org admin sees only public models, with no error. So if the MPM endpoint fails and the key is not an org admin, the two silent paths compose into a confident-looking low number rather than an unknown.

This is the failure mode the module sets out to prevent — "a false zero would read as a workspace that stopped using MPM" — and the fallback reintroduces it one level up. Suggest logging the exception at debug or warning, and distinguishing 401/403 from the rest, so a deployment where this never worked is visibly different from one where MPM is simply not installed.

Worth confirming on a deployment with comet.mpm.enabled true (it defaults false in the chart and is orthogonal to sagemaker.enabled) that the report can tell "MPM absent" from "auth refused". Right now it cannot.

Any failure of the member lookup used to be swallowed, with a silent fall
back to the registry. That included 401/403 (e.g. under SageMaker auth) and
proxy routing misses. A refused key that is also not an org admin then got
a confident-looking low count instead of a visible problem, because the
registry hides private models in non-member workspaces.

The lookup result is now classified: ok, refused (401/403), not_found (404:
MPM not installed or not routed), or error (anything else, including an
unexpected response shape). Each failure prints a warning naming the URL
and status; for refused, the warning also says the registry fallback may
undercount. The result is recorded as an mpm_member_lookup label KPI in
growth_org_kpis.csv, and in the HTML notes, including the Monitored models
card even when every workspace was checked.

http_error_status moves to cometx.utils so the MPM module can share it with
the chargeback error messages without a circular import. The module
docstring records why the /api/mpm/v3/workspaces path is fixed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dsblank

dsblank commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for tracing the SageMaker path. Agreed on the change you asked for; it's in 0afbeda.

The mpm/v3/workspaces lookup no longer fails silently. It's classified as one of:

Result When What happens
ok the call worked as before
refused 401 / 403 warning with the URL and status, plus a note that the registry fallback hides private models in non-member workspaces from users who aren't org admins, so counts may be low
not_found 404 warning that MPM may not be installed or routed on this deployment
error anything else, including an unexpected response shape warning with the error

The registry fallback still runs in every failure case, but the result is recorded:

  • CSV: a new mpm_member_lookup label KPI in growth_org_kpis.csv (metric_text = ok / refused / not_found / error), so a dashboard can tell "MPM absent" from "auth refused".
  • HTML: the MPM notes say so, e.g. "MPM API refused this key; counts may be low". That includes the Monitored models card, even when every workspace was checked.

I also recorded in the module docstring why the /api/mpm/v3/workspaces path is fixed (it's a backend-react route), since it reads like a bug.

Tested:

  • Unit tests for 401, 403, 404, 502, a network error, an unexpected response shape, the CSV and HTML output, and no KPI when --mpm isn't given. 340 tests pass.
  • Live on mpm-demo (comet.mpm.enabled) with an admin key: mpm_member_lookup=ok, 4 of 10 workspaces with 18 monitored models, 0 unchecked. I couldn't reproduce refused or not_found live there, so those are covered only by the unit tests. A check on a SageMaker deployment would still be worthwhile.

I kept the explicit Authorization header. As you say, it's redundant but harmless, and it matches the other admin calls (fetch_chargeback_report, service-accounts).

@baz-reviewer
baz-reviewer Bot dismissed their stale review September 30, 2026 19:28

Baz dismissed its prior approval because a re-review found new findings.

Comment thread cometx/utils.py
Nimrod007 and others added 2 commits October 1, 2026 10:36
Baz's finding on the classification added in 0afbeda, and it is right.

http_error_status fell back to parsing `status_code: NNN` out of the
exception's text when there was no response. Callers route on the result --
401/403 means the key was refused, 404 means MPM is not installed or not
routed -- so a status recovered from incidental text does not degrade to
"unknown". It asserts something specific and wrong, which is then recorded
in the mpm_member_lookup KPI and rendered in the HTML. A connection reset
quoting an inner frame became "MPM is not installed here".

That is the failure the classification was added to prevent, so the fallback
defeated the commit it shipped in.

Nothing is lost by refusing to guess, because the branch was unreachable for
the errors it was meant to serve. Every HTTP failure the SDK raises carries a
response: CometRestApiException.__init__ always assigns one, and NotFound and
Unauthorized both subclass it, so the first branch returns. Its own __str__
renders "failed with status code 404" -- no colon -- so the pattern would not
have matched that text even if it were reached. The branch could only ever
fire for a non-HTTP exception, where any match is incidental by definition.
One failure mode, no upside.

_short_api_error in admin_growth_report matches the same shape and keeps it
deliberately: there the number is only displayed, so a wrong match is
cosmetic rather than a misrouted classification. The docstring says so, since
the two now differ on purpose.

Four tests, including the connection-error case that regressed it. Verified
as guards: restoring the fallback fails the text case. pre-commit clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
My previous commit made http_error_status refuse to parse a status out of
an exception's text, and broke two chargeback tests doing it. The claim that
the text branch was unreachable was wrong: it holds for the MPM lookup, whose
errors come from the SDK and always carry a response, but not for the
chargeback path, which has an explicit test
(test_chargeback_status_parsed_from_text_when_no_response) asserting the
opposite. One shared helper was serving two callers that want different
answers, and I changed it for both.

They want different answers because the consequences differ. The MPM lookup
records its classification in the mpm_member_lookup KPI and renders it in the
HTML, so a guessed 404 asserts "MPM is not installed here" as a result.
_chargeback_error_message only chooses the wording of a sentence -- "this
needs an admin key" against "that URL is wrong" -- which the reader sees
beside the underlying error anyway, so a wrong guess costs a confusing line
and nothing more.

So: http_error_status stays strict and is what the MPM classification uses,
and apparent_http_status keeps the text fallback for callers that only shape
a message. Each docstring says which to reach for and why, since the pair is
otherwise an invitation to use whichever is nearer.

346 passing, up from 340 on 0afbeda, with the two tests my last commit broke
back to green. Verified against the full dependency set this time -- the
earlier run was missing reportlab, streamlit and boto3, which hid those two
failures behind 200-odd import errors and is why a red build went out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Nimrod007
Nimrod007 merged commit f7aec2b into main Oct 1, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants