Skip to content

fix(windows): inherit all system environment variables for subprocesses验证此改动能使dotnet正常运行#7054

Open
SoloLevelingAI wants to merge 6 commits intoAstrBotDevs:masterfrom
SoloLevelingAI:Fix-Dotnet
Open

fix(windows): inherit all system environment variables for subprocesses验证此改动能使dotnet正常运行#7054
SoloLevelingAI wants to merge 6 commits intoAstrBotDevs:masterfrom
SoloLevelingAI:Fix-Dotnet

Conversation

@SoloLevelingAI
Copy link
Copy Markdown

@SoloLevelingAI SoloLevelingAI commented Mar 27, 2026

问题描述

在Windows平台上,使用该库运行dotnet进程时出现启动失败。根本原因是_prepare_stdio_env函数只复制了PATHEXT环境变量,但许多程序(如dotnet)依赖其他系统环境变量(如PATH)才能正确运行。

重现步骤

  1. 在Windows系统上
  2. 尝试通过该库运行dotnet进程
  3. 观察进程启动失败或找不到dotnet可执行文件

根本原因

原代码仅检查并复制PATHEXT环境变量,忽略了其他必要的系统环境变量。这导致子进程的环境不完整,无法定位所需的可执行文件和依赖库。

解决方案

_prepare_stdio_env函数改为复制所有系统环境变量到子进程环境,并增加Dotnet的特殊分支判断:

  • 仅当该键不在用户自定义环境变量中时才复制
  • 保留了用户自定义环境变量的优先级
  • 为_prepare_stdio_env增加_extract_command_string、_merge_environment_variables、_ensure_dotnet_in_path、_create_subprocess多个Win平台和Dotnet的辅助函数

Screenshots or Test Results / 运行截图或测试结果

 [2026-03-29 10:20:02.315] [Core] [INFO] [provider.func_tool_manager:648]: Disconnected from MCP server C# MCP
[2026-03-29 10:20:13.420] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] info: ModelContextProtocol.Server.StdioServerTransport[857250842]
[2026-03-29 10:20:13.421] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Server (stream) (McpServer1) transport reading messages.
[2026-03-29 10:20:13.475] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] info: Microsoft.Hosting.Lifetime[0]
[2026-03-29 10:20:13.475] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Application started. Press Ctrl+C to shut down.
[2026-03-29 10:20:13.476] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] info: Microsoft.Hosting.Lifetime[0]
[2026-03-29 10:20:13.476] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Hosting environment: Production
[2026-03-29 10:20:13.476] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Content root path: C:\Users\Admin\.astrbot_launcher\instances\99359ee2-f78f-4a72-8a6b-e698e6826869\core
[2026-03-29 10:20:13.486] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] info: ModelContextProtocol.Server.McpServer[570385771]
[2026-03-29 10:20:13.486] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Server (McpServer1 1.0.0.0) method 'initialize' request handler called.
[2026-03-29 10:20:13.503] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] info: ModelContextProtocol.Server.McpServer[570385771]
[2026-03-29 10:20:13.504] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Server (McpServer1 1.0.0.0), Client (mcp 0.1.0) method 'tools/list' request handler called.
[2026-03-29 10:20:13.508] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] info: ModelContextProtocol.Server.McpServer[1867955179]
[2026-03-29 10:20:13.508] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Server (McpServer1 1.0.0.0), Client (mcp 0.1.0) method 'initialize' request handler completed in 16.9215ms.
[2026-03-29 10:20:13.525] [Core] [INFO] [provider.func_tool_manager:628]: Connected to MCP server C# MCP, Tools: ['get_random_number', 'get_ce', 'get_ce_member', 'get_city_weather']
[2026-03-29 10:20:13.525] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] info: ModelContextProtocol.Server.McpServer[1867955179]
[2026-03-29 10:20:13.525] [Core] [INFO] [utils.log_pipe:31]: [MCPServer-C# MCP] Server (McpServer1 1.0.0.0), Client (mcp 0.1.0) method 
image

Modifications / 改动点

This is NOT a breaking change. / 这不是一个破坏性变更。

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Preserve a complete and usable Windows environment for stdio-based subprocesses to ensure tools like dotnet start correctly.

Bug Fixes:

  • Fix Windows subprocess startup failures by inheriting all system environment variables instead of only PATHEXT.
  • Ensure dotnet can be located by augmenting PATH with common installation directories when necessary.
  • Prevent Windows stdio subprocesses from opening console windows by default via appropriate creation flags.

Enhancements:

  • Automatically infer and set an appropriate working directory for stdio subprocesses based on provided project arguments when no cwd is specified.

问题:在Windows上,dotnet子进程因环境变量不完整而无法运行。
原因:原函数只设置了PATHEXT,但dotnet可能需要其他系统环境变量(如PATH)。
解决方案:将系统环境变量复制到子进程环境变量中,确保子进程有完整的运行环境。
测试:说明您已经验证此改动能使dotnet正常运行。
@auto-assign auto-assign bot requested review from advent259141 and anka-afk March 27, 2026 15:34
@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Mar 27, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • The _prepare_stdio_env helper is becoming quite Windows- and dotnet-specific (PATH patching, project arg parsing, CREATE_NO_WINDOW), which makes it harder to reason about; consider splitting dotnet-specific logic and process-creation flags into separate, opt-in helpers or call sites so this stays a generic environment-prep function.
  • Automatically modifying PATH (searching for dotnet and prepending common install locations) may surprise callers that deliberately control the environment; it might be safer to only inherit os.environ and let the caller decide whether to adjust PATH or inject dotnet-specific paths.
  • The large commented-out block of the previous implementation at the top of _prepare_stdio_env adds noise; if it’s no longer needed, remove it and rely on version control history instead.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `_prepare_stdio_env` helper is becoming quite Windows- and dotnet-specific (PATH patching, project arg parsing, CREATE_NO_WINDOW), which makes it harder to reason about; consider splitting dotnet-specific logic and process-creation flags into separate, opt-in helpers or call sites so this stays a generic environment-prep function.
- Automatically modifying `PATH` (searching for dotnet and prepending common install locations) may surprise callers that deliberately control the environment; it might be safer to only inherit `os.environ` and let the caller decide whether to adjust PATH or inject dotnet-specific paths.
- The large commented-out block of the previous implementation at the top of `_prepare_stdio_env` adds noise; if it’s no longer needed, remove it and rely on version control history instead.

## Individual Comments

### Comment 1
<location path="astrbot/core/agent/mcp_client.py" line_range="67-71" />
<code_context>
-    if not pathext:
-        return config
-
     prepared = config.copy()
     env = dict(prepared.get("env") or {})
-    env.setdefault("PATHEXT", pathext)
+    
+    # 获取系统的环境变量
+    for key in os.environ:
+        if key not in env:
+            env[key] = os.environ[key]
</code_context>
<issue_to_address>
**issue (bug_risk):** Environment merge logic may misbehave on Windows due to case-insensitive env var names.

Because Windows treats env var names case-insensitively but this dict is case-sensitive, `if key not in env` can create duplicates like `Path` vs `PATH` or break overrides when the caller uses different casing than `os.environ`. Consider building `env` via `env = os.environ.copy()` and then `env.update(user_env)` so user-specified values override system ones while keeping a consistent key casing.
</issue_to_address>

### Comment 2
<location path="astrbot/core/agent/mcp_client.py" line_range="75-76" />
<code_context>
+        if key not in env:
+            env[key] = os.environ[key]
+    
+    # 确保dotnet在PATH中
+    if "PATH" in env:
+        # 检查dotnet是否在PATH中
+        import shutil
</code_context>
<issue_to_address>
**issue:** Handling only `PATH` and not `Path` may miss the Windows PATH variable.

On Windows, environment variable names are case-sensitive in the dict, and the variable is commonly exposed as `Path`. Restricting the check to `"PATH"` can skip `dotnet` resolution on Windows. Consider a case-insensitive lookup for PATH, or explicitly support both `"PATH"` and `"Path"` when checking/setting this value.
</issue_to_address>

### Comment 3
<location path="astrbot/core/agent/mcp_client.py" line_range="106-108" />
<code_context>
+                break
+    
+    # Windows进程创建标志
+    if "creationflags" not in prepared:
+        import subprocess
+        prepared["creationflags"] = subprocess.CREATE_NO_WINDOW
+    
     return prepared
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Defaulting to `CREATE_NO_WINDOW` may conflict with callers’ expectations for console behavior.

This unconditionally changes the Windows subprocess defaults and could break callers that rely on a console window, attaching to an existing console, or combining their own flags. Please gate this behind an explicit option (e.g., config / parameter) or only apply it when a non-interactive/background mode is clearly requested.

Suggested implementation:

```python
    # Windows 进程创建标志:仅在显式请求无控制台窗口时设置
    if os.name == "nt" and prepared.get("no_console", False):
        import subprocess
        existing_flags = prepared.get("creationflags", 0)
        prepared["creationflags"] = existing_flags | subprocess.CREATE_NO_WINDOW

```

To fully support this behavior, ensure that:
1. Call sites that need a hidden console window explicitly pass `{"no_console": True}` into the `prepared` mapping (or whatever structure is used to construct `prepared`).
2. If `os` is not already imported at the top of `astrbot/core/agent/mcp_client.py`, add `import os` there.
</issue_to_address>

### Comment 4
<location path="astrbot/core/agent/mcp_client.py" line_range="50" />
<code_context>


 def _prepare_stdio_env(config: dict) -> dict:
+    # """Preserve Windows executable resolution for stdio subprocesses."""
+    # if sys.platform != "win32":
</code_context>
<issue_to_address>
**issue (complexity):** Consider splitting `_prepare_stdio_env` into smaller helper functions so it only orchestrates env merging, PATH/dotnet setup, cwd inference, and Windows flags.

You’ve significantly increased the responsibilities of `_prepare_stdio_env`; it’s now doing environment merging, PATH/dotnet patching, cwd inference, and Windows-specific creation flags. This makes it harder to reason about and change safely.

You can keep all behavior but reduce complexity by:

1. **Split responsibilities into small helpers**  
2. **Make `_prepare_stdio_env` a thin orchestrator**  
3. **Remove commented-out legacy code (kept in git history anyway)**  

For example:

```python
def _merge_env_with_system(config: dict) -> dict:
    prepared = config.copy()
    env = dict(prepared.get("env") or {})

    for key, value in os.environ.items():
        env.setdefault(key, value)

    prepared["env"] = env
    return prepared


def _ensure_dotnet_in_path(config: dict) -> dict:
    prepared = config.copy()
    env = dict(prepared.get("env") or {})

    path = env.get("PATH")
    if not path:
        prepared["env"] = env
        return prepared

    import shutil
    if shutil.which("dotnet", path=path):
        prepared["env"] = env
        return prepared

    common_dotnet_paths = [
        r"C:\Program Files\dotnet",
        r"C:\Program Files (x86)\dotnet",
        os.path.expanduser(r"~\.dotnet\tools"),
    ]
    existing = [p for p in common_dotnet_paths if os.path.exists(p)]
    if existing:
        env["PATH"] = ";".join(existing + [path])

    prepared["env"] = env
    return prepared
```

```python
def _infer_cwd_from_project_args(config: dict) -> dict:
    prepared = config.copy()
    if "cwd" in prepared:
        return prepared

    args = prepared.get("args", [])
    for i, arg in enumerate(args):
        if arg == "--project" and i + 1 < len(args):
            project_path = args[i + 1]
            if os.path.exists(project_path):
                prepared["cwd"] = os.path.dirname(project_path)
                logger.info("Setting working directory to: %s", prepared["cwd"])
            break

    return prepared


def _apply_windows_creationflags(config: dict) -> dict:
    prepared = config.copy()
    if "creationflags" in prepared:
        return prepared

    import subprocess
    prepared["creationflags"] = subprocess.CREATE_NO_WINDOW
    return prepared
```

Then `_prepare_stdio_env` becomes much easier to read and maintain:

```python
def _prepare_stdio_env(config: dict) -> dict:
    """Prepare Windows stdio subprocess config (env, cwd, creationflags)."""
    if sys.platform != "win32":
        return config

    prepared = _merge_env_with_system(config)
    prepared = _ensure_dotnet_in_path(prepared)
    prepared = _infer_cwd_from_project_args(prepared)
    prepared = _apply_windows_creationflags(prepared)
    return prepared
```

This keeps all current behavior but:

- Makes each concern individually testable.
- Reduces hidden coupling inside `_prepare_stdio_env`.
- Makes future changes (e.g., tweaking cwd inference or dotnet detection) less risky.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dosubot dosubot bot added the area:core The bug / feature is about astrbot's core, backend label Mar 27, 2026
@dosubot
Copy link
Copy Markdown

dosubot bot commented Mar 27, 2026

Related Documentation

1 document(s) may need updating based on files changed in this PR:

AstrBotTeam's Space

pr4697的改动
View Suggested Changes
@@ -606,6 +606,70 @@
 - 环境变量 `ASTRBOT_MCP_INIT_STRICT` 可配置为 `1`、`true`、`yes` 或 `on` 启用严格模式
 - 严格模式下,如果所有 MCP 服务初始化失败,系统会中止启动
 - 非严格模式下(默认),MCP 初始化失败仅记录警告,系统继续启动
+
+#### Windows 子进程环境变量修复(PR #7054)
+
+[PR #7054](https://github.com/AstrBotDevs/AstrBot/pull/7054) 修复了 Windows 平台上 MCP 客户端子进程环境变量处理的关键问题,确保 dotnet 等程序能够正常运行。
+
+**问题背景**
+
+修复前,`_prepare_stdio_env()` 函数在 Windows 平台上仅复制 `PATHEXT` 环境变量到子进程,忽略了其他系统环境变量(如 `PATH`)。这导致许多依赖系统环境变量的程序(如 dotnet、node 等)在 MCP 子进程中无法正常启动,提示找不到可执行文件。
+
+**修复实现**
+
+修复后,系统会继承所有系统环境变量到 MCP 子进程:
+
+```python
+def _prepare_stdio_env(config: dict) -> dict:
+    if sys.platform != "win32":
+        return config
+    prepared = config.copy()
+    env = dict(prepared.get("env") or {})
+    
+    # 继承所有系统环境变量
+    for key in os.environ:
+        if key not in env:
+            env[key] = os.environ[key]
+    
+    prepared["env"] = env
+    return prepared
+```
+
+该实现确保子进程获得完整的环境上下文,同时保留用户自定义环境变量的优先级(用户定义的环境变量不会被系统变量覆盖)。
+
+**增强功能**
+
+除环境变量继承外,PR #7054 还引入了以下 Windows 平台增强:
+
+1. **自动 dotnet 路径检测**:
+   - 如果 `PATH` 中未找到 dotnet,系统会自动检测并添加常见的 dotnet 安装路径:
+     - `C:\Program Files\dotnet`
+     - `C:\Program Files (x86)\dotnet`
+     - `~\.dotnet\tools`(用户目录)
+   - 检测到可用路径后自动添加到子进程的 `PATH` 环境变量
+
+2. **工作目录自动推断**:
+   - 从 `--project` 参数自动推断工作目录
+   - 例如:`--project path/to/project.csproj` 会自动设置工作目录为 `path/to/`
+   - 避免因工作目录不正确导致的文件访问错误
+
+3. **静默进程创建**:
+   - 在 Windows 上设置 `CREATE_NO_WINDOW` 进程创建标志
+   - 避免 MCP 子进程弹出控制台窗口,提升用户体验
+
+**影响范围**
+
+该修复提升了 Windows 平台上 MCP 服务器的兼容性和可靠性:
+
+- **dotnet 类 MCP 服务器**:C# 编写的 MCP 服务器现在可以正常启动和运行
+- **其他依赖系统环境的 MCP 服务器**:node、python、java 等运行时的 MCP 服务器环境变量完整性得到保障
+- **用户体验提升**:静默进程创建和自动路径检测减少了手动配置需求
+
+**技术要点**
+
+- **环境变量继承顺序**:用户自定义环境变量 → 系统环境变量,确保用户配置优先级
+- **路径规范化**:自动添加的 dotnet 路径会附加到 `PATH` 的开头,确保优先使用
+- **跨平台兼容**:该增强仅在 Windows 平台生效,不影响 Linux/macOS 的行为
 
 #### 后台任务执行流程(PR #5722)
 

[Accept] [Decline]

Note: You must be authenticated to accept/decline updates.

How did I do? Any feedback?  Join Discord

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates _prepare_stdio_env to inherit system environment variables and introduces logic to handle dotnet paths, working directory inference, and Windows process creation flags. Feedback highlights that the dotnet-specific logic should be decoupled from this general-purpose function, suggests removing dead commented-out code, recommends moving module imports to the top of the file per PEP 8, and provides a code suggestion to prevent redundant PATH modifications by adding a break statement.

Comment on lines +75 to +109
# 确保dotnet在PATH中
if "PATH" in env:
# 检查dotnet是否在PATH中
import shutil
dotnet_path = shutil.which("dotnet", path=env["PATH"])
if not dotnet_path:
# 尝试添加常见的dotnet安装路径
common_dotnet_paths = [
r"C:\Program Files\dotnet",
r"C:\Program Files (x86)\dotnet",
os.path.expanduser(r"~\.dotnet\tools")
]
for dotnet_dir in common_dotnet_paths:
if os.path.exists(dotnet_dir):
env["PATH"] = f"{dotnet_dir};{env['PATH']}"

prepared["env"] = env

# 设置工作目录
if "cwd" not in prepared:
# 尝试从项目路径推断工作目录
args = prepared.get("args", [])
for i, arg in enumerate(args):
if arg == "--project" and i + 1 < len(args):
project_path = args[i + 1]
if os.path.exists(project_path):
prepared["cwd"] = os.path.dirname(project_path)
logger.info(f"Setting working directory to: {prepared['cwd']}")
break

# Windows进程创建标志
if "creationflags" not in prepared:
import subprocess
prepared["creationflags"] = subprocess.CREATE_NO_WINDOW

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

你好,这个 Pull Request 的主要目标是继承系统环境变量,这在 71-73 行已经解决了。然而,从 75 行到 109 行的新增代码引入了许多针对 dotnet 应用的特定逻辑。例如硬编码的 dotnet 路径、从 --project 参数推断工作目录(cwd),以及设置 Windows 特有的 creationflags

这使得 _prepare_stdio_env 函数的通用性大大降低,并与一个特定的使用场景(运行 dotnet)紧密耦合。这种特定于应用程序的逻辑,最好由调用方(它知道自己正在运行一个 dotnet 进程)通过配置 config 字典来处理,而不是硬编码在一个通用函数中。

建议将 these dotnet 相关的特定逻辑移出此函数,以保持其通用性和可维护性。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已拆分为辅助函数

Comment on lines +51 to +63
# """Preserve Windows executable resolution for stdio subprocesses."""
# if sys.platform != "win32":
# return config

# pathext = os.environ.get("PATHEXT")
# if not pathext:
# return config

# prepared = config.copy()
# env = dict(prepared.get("env") or {})
# env.setdefault("PATHEXT", pathext)
# prepared["env"] = env
# return prepared
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

这里有一大块被注释掉的代码,看起来是之前的实现。为了提高代码的整洁度和可读性,建议删除这些不再使用的“死代码”。版本控制系统(如 Git)可以很好地追踪历史代码,所以不必在代码中保留它们。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已解决。

…入输出):改进 Windows 环境设置以支持子进程

- Merge environment variables case‑insensitively
- Auto‑configure dotnet PATH and suppress console window
- Keep PATHEXT inheritance (from old implementation)

Replaces the minimal PATHEXT fix with a robust solution that resolves
common Windows subprocess issues, especially for .NET servers.
- Merge environment variables case‑insensitively
- Auto‑configure dotnet PATH and suppress console window
- Keep PATHEXT inheritance (from old implementation)
-Add `show_console` option to control `CREATE_NO_WINDOW` flag.Default remains hidden (backward compatible) but can be overridden.

Replaces the minimal PATHEXT fix with a robust solution that resolves
common Windows subprocess issues, especially for .NET servers.
…入输出):改进 Windows 环境设置以支持子进程

- Merge environment variables case‑insensitively
- Auto‑configure dotnet PATH and suppress console window
- Keep PATHEXT inheritance (from old implementation)
-Add `show_console` option to control `CREATE_NO_WINDOW` flag.Default remains hidden (backward compatible) but can be overridden.

Replaces the minimal PATHEXT fix with a robust solution that resolves
common Windows subprocess issues, especially for .NET servers.
…入输出):改进 Windows 环境设置以支持子进程

- Merge environment variables case‑insensitively
- Auto‑configure dotnet PATH and suppress console window
- Keep PATHEXT inheritance (from old implementation)
-Add `no_console` option to control `CREATE_NO_WINDOW` flag.Default remains hidden (backward compatible) but can be overridden.

Replaces the minimal PATHEXT fix with a robust solution that resolves
common Windows subprocess issues, especially for .NET servers.
…入输出):改进 Windows 环境设置以支持子进程

- Merge environment variables case‑insensitively
- Auto‑configure dotnet PATH and suppress console window
- Keep PATHEXT inheritance (from old implementation)
-Add `no_console` option to control `CREATE_NO_WINDOW` flag.Default remains hidden (backward compatible) but can be overridden.

Replaces the minimal PATHEXT fix with a robust solution that resolves
common Windows subprocess issues, especially for .NET servers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant