For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 让 operate 与 control 共享的 7 个连接键收敛为唯一数据源 tl-shared.config(换机只改一处),并清理 operate 端 12 个换气/CCD 死副本键。
Architecture: 方案一——新建共享 appSettings 片段 tl-shared.config 放 operate 输出根;operate/control 各自 App.config 用 <appSettings file="…tl-shared.config"> 只读合并(两进程读取 C# 零改动);写入由 operate 侧 AppConfigHelper 经新纯逻辑单元 SharedConfigStore(XDocument 直写)收口。control 仅加一行 XML、C# 与 csproj 均不改。凭据组/front/Java/oplog-config 本轮不动。
Tech Stack: C# / .NET 6 (net6.0-windows) / WPF / xUnit / System.Configuration / System.Xml.Linq。
对应设计: 项目文档/需求文档/specs/2026-06-23-配置收敛-operate-control连接组单一数据源-design.md
分支: feature/config-consolidation(已建,spec 已提交 f547ac9)。
真机口径: UAC 静默可提权、当前无 control 在跑/无活体培养,可自由启停;EEPROM/连接验证无电机风险。验证用部署布局(control 在 operate 根的 control\ 子目录)。
ivf_tl_operate_2.0/ivf_tl_Operate/tl-shared.config —— 唯一数据源(7 连接键)。ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/SharedConfigStore.cs —— 纯 XDocument 读写 appSettings 片段。ivf_tl_operate_2.0/ivf_tl_Operate.Tests/(xUnit)—— operate 首个单测工程,测 SharedConfigStore。ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/AppConfigHelper.cs —— Save 按 key 归属分流。ivf_tl_operate_2.0/ivf_tl_Operate/ivf_tl_Operate.csproj —— 拷贝 tl-shared.config 到输出根。ivf_tl_operate_2.0/ivf_tl_Operate/App.config —— 加 file= + 删 7 共享键 + 删 12 死键。ivf_tl_operate_2.0/control/ivf_tl_ControlHost/App.config —— 加 file="..\tl-shared.config" + 删 7 共享键。开发环境/双进程部署指南.md、开发环境/连接配置清单-换服务器必读.md、续接三件套。<appSettings file="..\x.config"> 子目录可读父目录(破假设)方案一命脉。过不了当场退方案二。临时 harness 放 gitignore 的
临时文件/,不入库。
Files:
临时文件/ConfigFileProbe/ConfigFileProbe.csproj临时文件/ConfigFileProbe/Program.csCreate: 临时文件/ConfigFileProbe/App.config
[ ] Step 1: 写 harness 工程
临时文件/ConfigFileProbe/ConfigFileProbe.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
</ItemGroup>
</Project>
临时文件/ConfigFileProbe/App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings file="..\probe-shared.config">
<add key="inlineKey" value="INLINE"/>
</appSettings>
</configuration>
临时文件/ConfigFileProbe/Program.cs:
using System;
using System.Configuration;
class Program
{
static int Main()
{
var shared = ConfigurationManager.AppSettings["probeKey"];
var inline = ConfigurationManager.AppSettings["inlineKey"];
Console.WriteLine($"probeKey={shared}|inlineKey={inline}");
return shared == "HELLO" ? 0 : 1;
}
}
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/临时文件/ConfigFileProbe"
dotnet build -c Release
# 模拟部署布局:把 exe 放进 sub/,共享文件放 sub 的父目录
mkdir -p deploy/sub
cp bin/Release/net6.0-windows/ConfigFileProbe.exe deploy/sub/
cp bin/Release/net6.0-windows/ConfigFileProbe.dll deploy/sub/
cp bin/Release/net6.0-windows/ConfigFileProbe.runtimeconfig.json deploy/sub/
cp bin/Release/net6.0-windows/ConfigFileProbe.dll.config deploy/sub/
cp bin/Release/net6.0-windows/System.Configuration.ConfigurationManager.dll deploy/sub/ 2>/dev/null || true
printf '<?xml version="1.0" encoding="utf-8"?>\n<appSettings>\n <add key="probeKey" value="HELLO"/>\n</appSettings>\n' > deploy/probe-shared.config
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/临时文件/ConfigFileProbe/deploy/sub"
./ConfigFileProbe.exe; echo "exit=$?"
Expected: 输出 probeKey=HELLO|inlineKey=INLINE,exit=0。
✅ 过 →
file=+..子目录可读父目录成立,继续 Task 2。 ❌ 不过(probeKey 空/exit=1)→ 停下,退回方案二(ProgramData+自写加载器),回写 spec 与本计划,不要继续。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0" && git status --porcelain 临时文件/ | head
Expected: 无输出(临时文件/ 已被 gitignore)。无需提交。
Files:
ivf_tl_operate_2.0/ivf_tl_Operate.Tests/ivf_tl_Operate.Tests.csprojivf_tl_operate_2.0/ivf_tl_Operate.Tests/SharedConfigStoreTests.csCreate: ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/SharedConfigStore.cs
[ ] Step 1: 建 operate 单测工程并加入 sln
ivf_tl_operate_2.0/ivf_tl_Operate.Tests/ivf_tl_Operate.Tests.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>disable</Nullable>
<IsPackable>false</IsPackable>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.6.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ivf_tl_Operate\ivf_tl_Operate.csproj" />
</ItemGroup>
</Project>
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
dotnet sln ivf_tl_Operate.sln add ivf_tl_Operate.Tests/ivf_tl_Operate.Tests.csproj
Expected: Project ... added。
ivf_tl_operate_2.0/ivf_tl_Operate.Tests/SharedConfigStoreTests.cs:
using System.IO;
using ivf_tl_Operate.Helpers;
using Xunit;
namespace ivf_tl_Operate.Tests
{
public class SharedConfigStoreTests
{
private static string TempFile() =>
Path.Combine(Path.GetTempPath(), "tl-shared-test-" + Path.GetRandomFileName() + ".config");
[Fact]
public void Read_missing_file_returns_null()
{
var path = TempFile();
Assert.Null(SharedConfigStore.Read(path, "urlIp"));
}
[Fact]
public void Write_then_Read_roundtrips()
{
var path = TempFile();
try
{
SharedConfigStore.Write(path, "urlIp", "http://10.0.0.5");
SharedConfigStore.Write(path, "urlPort", "10010");
Assert.Equal("http://10.0.0.5", SharedConfigStore.Read(path, "urlIp"));
Assert.Equal("10010", SharedConfigStore.Read(path, "urlPort"));
}
finally { File.Delete(path); }
}
[Fact]
public void Write_existing_key_updates_value()
{
var path = TempFile();
try
{
SharedConfigStore.Write(path, "mqttIp", "192.168.0.108");
SharedConfigStore.Write(path, "mqttIp", "192.168.0.200");
Assert.Equal("192.168.0.200", SharedConfigStore.Read(path, "mqttIp"));
}
finally { File.Delete(path); }
}
[Fact]
public void Written_file_is_appSettings_fragment()
{
var path = TempFile();
try
{
SharedConfigStore.Write(path, "kfkaIP", "192.168.0.108");
var text = File.ReadAllText(path);
Assert.Contains("<appSettings>", text);
Assert.Contains("key=\"kfkaIP\"", text);
}
finally { File.Delete(path); }
}
}
}
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
dotnet test ivf_tl_Operate.Tests/ivf_tl_Operate.Tests.csproj
Expected: 编译失败 CS0103/CS0246 SharedConfigStore 不存在 = RED。
ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/SharedConfigStore.cs:
using System.IO;
using System.Xml.Linq;
namespace ivf_tl_Operate.Helpers
{
/// <summary>
/// 配置收敛:operate↔control 共享连接键的唯一数据源文件(tl-shared.config)读写。
/// 文件格式 = 独立 appSettings 片段(<appSettings><add key.. value../></appSettings>),
/// 正是 <appSettings file="…"> 期望的外部文件格式 → 两进程经 file= 只读合并即读到。
/// 用 XDocument 直写,绕开 ConfigurationManager 写回 file= 的不确定行为。
/// </summary>
public static class SharedConfigStore
{
/// <summary>读片段文件里某键值;文件不存在或键缺失返回 null。</summary>
public static string Read(string path, string key)
{
if (!File.Exists(path)) return null;
try
{
var doc = XDocument.Load(path);
foreach (var add in doc.Descendants("add"))
{
if ((string)add.Attribute("key") == key)
return (string)add.Attribute("value");
}
return null;
}
catch
{
return null;
}
}
/// <summary>写某键值(存在则更新,不存在则新增);文件不存在则创建片段骨架。</summary>
public static void Write(string path, string key, string value)
{
XDocument doc;
if (File.Exists(path))
{
try { doc = XDocument.Load(path); }
catch { doc = NewDoc(); }
}
else
{
doc = NewDoc();
}
var root = doc.Root ?? new XElement("appSettings");
if (doc.Root == null) doc.Add(root);
XElement target = null;
foreach (var add in root.Elements("add"))
{
if ((string)add.Attribute("key") == key) { target = add; break; }
}
if (target == null)
{
target = new XElement("add", new XAttribute("key", key), new XAttribute("value", value ?? ""));
root.Add(target);
}
else
{
target.SetAttributeValue("value", value ?? "");
}
doc.Save(path);
}
private static XDocument NewDoc() =>
new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("appSettings"));
}
}
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
dotnet test ivf_tl_Operate.Tests/ivf_tl_Operate.Tests.csproj
Expected: Passed! - Failed: 0, Passed: 4。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0"
git add ivf_tl_operate_2.0/ivf_tl_Operate.Tests ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/SharedConfigStore.cs ivf_tl_operate_2.0/ivf_tl_Operate.sln
git commit -m "feat(config): SharedConfigStore 共享配置片段读写 + operate 单测工程(TDD 4 绿)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
读取保持不变(经 ConfigurationManager 的 file= 合并);只改 Save 的落点。
Files:
Modify: ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/AppConfigHelper.cs
[ ] Step 1: 加共享键集合 + 共享文件路径 + Save 分流
在 AppConfigHelper 类内,把现有 Save 方法(约 :58-74)替换为下面这段(新增 SharedKeys/SharedConfigPath/SaveShared,改 Save):
/// <summary>配置收敛:operate↔control 共享的连接键(唯一数据源 tl-shared.config)。</summary>
private static readonly System.Collections.Generic.HashSet<string> SharedKeys =
new System.Collections.Generic.HashSet<string>(System.StringComparer.OrdinalIgnoreCase)
{ "urlIp", "urlPort", "mqttIp", "mqttPort", "kfkaIP", "kfkaPort", "outInter" };
/// <summary>共享文件路径 = operate 输出根目录\tl-shared.config(control 经 ..\ 指此)。</summary>
public static string SharedConfigPath =>
System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "tl-shared.config");
/// <summary>
/// 写回普通键。配置收敛:共享键落唯一数据源 tl-shared.config(SharedConfigStore),
/// 非共享键沿用 OpenExeConfiguration 落 operate 自己的 config。写后刷新本进程缓存。
/// </summary>
public static void Save(string key, string value)
{
try
{
if (SharedKeys.Contains(key))
{
SharedConfigStore.Write(SharedConfigPath, key, value);
}
else
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (config.AppSettings.Settings[key] == null)
config.AppSettings.Settings.Add(key, value);
else
config.AppSettings.Settings[key].Value = value;
config.Save(ConfigurationSaveMode.Modified);
}
ConfigurationManager.RefreshSection("appSettings");
}
catch
{
// 写失败不抛出(避免阻断 UI/启动);[M7] 运行环境核查落盘。
}
}
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
dotnet build ivf_tl_Operate/ivf_tl_Operate.csproj -c Release
Expected: Build succeeded. 0 Error(s)。(若报 MSB3021 DLL 锁,先停掉在跑的 operate/control 再编。)
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
dotnet test ivf_tl_Operate.Tests/ivf_tl_Operate.Tests.csproj
Expected: Passed: 4。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0"
git add ivf_tl_operate_2.0/ivf_tl_Operate/Helpers/AppConfigHelper.cs
git commit -m "feat(config): AppConfigHelper.Save 共享键落 tl-shared.config、非共享键不变
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
Files:
ivf_tl_operate_2.0/ivf_tl_Operate/tl-shared.configivf_tl_operate_2.0/ivf_tl_Operate/ivf_tl_Operate.csproj(在已有 oplog-config.json 的 ItemGroup 旁加一项)Modify: ivf_tl_operate_2.0/ivf_tl_Operate/App.config
[ ] Step 1: 创建唯一数据源 tl-shared.config(当前 108 值)
ivf_tl_operate_2.0/ivf_tl_Operate/tl-shared.config:
<?xml version="1.0" encoding="utf-8"?>
<!-- 配置收敛:operate↔control 共享连接键的【唯一数据源】。
换中间件服务器/改连接参数只改本文件一处。
operate 经 App.config <appSettings file="tl-shared.config"> 读;
control 经 ..\tl-shared.config 读。两进程读取代码零改动。 -->
<appSettings>
<add key="urlIp" value="http://127.0.0.1"/>
<add key="urlPort" value="10010"/>
<add key="mqttIp" value="192.168.0.108"/>
<add key="mqttPort" value="1883"/>
<add key="kfkaIP" value="192.168.0.108"/>
<add key="kfkaPort" value="9092"/>
<add key="outInter" value="0"/>
</appSettings>
在 ivf_tl_operate_2.0/ivf_tl_Operate/ivf_tl_Operate.csproj 已有的 oplog-config.json 那段(:171-174 附近)下方,同一 <ItemGroup> 内追加:
<None Remove="tl-shared.config" />
<Content Include="tl-shared.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
把 ivf_tl_operate_2.0/ivf_tl_Operate/App.config 的 <appSettings> 开标签改为:
<appSettings file="tl-shared.config">
然后删除这 7 个共享键行(连同其上的注释):outInter、urlIp、urlPort、mqttIp、mqttPort、kfkaIP、kfkaPort。
再删除这 12 个换气/CCD 死键行(连同注释):CCDError、csTime、gbTime、VentNum、VentPre、VentWaitTimeB、VentWaitTimeD、AutoWaitTime、CCDAutoWaitTime、CCDFailedWaitTime、CCDFailedNumber、QueuAir。
保留(operate 独有):autoFocus、userName、passWord、engineerPwd、tlNum、houseEnabled、Language、StopPro、cacheDisk、controlPort、controlExePath。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/ivf_tl_Operate"
for k in CCDError csTime gbTime VentNum VentPre VentWaitTimeB VentWaitTimeD AutoWaitTime CCDAutoWaitTime CCDFailedWaitTime CCDFailedNumber QueuAir; do
echo "== $k =="; grep -rn "AppSettings\[\"$k\"\]\|GetString(\"$k\"\|GetInt(\"$k\"" . --include=*.cs;
done
Expected: 每个键无输出(operate 端不消费)。若某键有输出 → 该键不删、保留并在交接卡记录例外。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
dotnet build ivf_tl_Operate/ivf_tl_Operate.csproj -c Release
Expected: Build succeeded. 0 Error(s);输出目录 bin/Release/net6.0-windows/tl-shared.config 存在。
Run(确认落盘):
ls -l "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/ivf_tl_Operate/bin/Release/net6.0-windows/tl-shared.config"
Expected: 文件存在。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0"
git add ivf_tl_operate_2.0/ivf_tl_Operate/tl-shared.config ivf_tl_operate_2.0/ivf_tl_Operate/ivf_tl_Operate.csproj ivf_tl_operate_2.0/ivf_tl_Operate/App.config
git commit -m "feat(config): operate 接 tl-shared.config(file=)+删7共享键+删12换气CCD死键
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
control 只动 App.config 一处:加 file= 指父目录 + 删共享键。C#/csproj 不动。换气/CCD 业务键 control 仍读 → 保留。
Files:
Modify: ivf_tl_operate_2.0/control/ivf_tl_ControlHost/App.config
[ ] Step 1: control App.config 接 file= + 删 7 共享键
把 ivf_tl_operate_2.0/control/ivf_tl_ControlHost/App.config 的 <appSettings> 开标签改为:
<appSettings file="..\tl-shared.config">
然后删除这 7 个共享键行(连同注释):urlIp、urlPort、mqttIp、mqttPort、kfkaIP、kfkaPort、outInter。
保留(control 独有,缺则 NPE):userName、passWord、engineerPwd、tlNum、houseEnabled、Language、autoFocus、CCDError、csTime、gbTime、VentNum、VentPre、VentWaitTimeB、VentWaitTimeD、AutoWaitTime、CCDAutoWaitTime、CCDFailedWaitTime、CCDFailedNumber、QueuAir、StopPro、cacheDisk、controlPort。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0/control"
dotnet build ivf_tl_Control.sln -c Release
Expected: Build succeeded. 0 Error(s)。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0"
git add ivf_tl_operate_2.0/control/ivf_tl_ControlHost/App.config
git commit -m "feat(config): control 接 ..\\tl-shared.config(file=)+删7共享键,C#零改动
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
验证用部署布局:operate 输出根 + 其下
control\(ControlHost)+ 根的tl-shared.config。UAC 静默提权,无活体培养可自由启停。
Run(部署 control 到 operate 根的 control\ 子目录、确认共享文件就位):
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
OPDIR="ivf_tl_Operate/bin/Release/net6.0-windows"
mkdir -p "$OPDIR/control"
cp -r control/ivf_tl_ControlHost/bin/Release/net6.0-windows/* "$OPDIR/control/"
ls -l "$OPDIR/tl-shared.config" "$OPDIR/control/ivf_tl_ControlHost.dll.config"
Expected: 两文件都在。control/ 下没有 tl-shared.config(靠 ..\ 指父目录)。
用 Task1 的方式直接验 control 端读取:提权静默启动 control.exe,查 /status 连接来自共享文件的值。先把共享文件 mqttIp 改个可辨识值再启。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
OPDIR="ivf_tl_Operate/bin/Release/net6.0-windows"
# 提权启动 control(独立进程,读 ..\tl-shared.config)
powershell -Command "Start-Process -Verb RunAs -FilePath '$PWD/$OPDIR/control/ivf_tl_ControlHost.exe'"
sleep 12
curl -s http://127.0.0.1:38080/status; echo
Expected: /status 返回 {"ok":true,"pid":…,"tlSn":"NEO-1-20230411","started":…},证明 control 读到了 ..\tl-shared.config 的 url/gateway(否则连不上、started 不会 true / 进程起不来)。
Run(停 control → 改共享文件 kfkaIP 为占位 → 重启 → 看日志/连接反映新值):
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
OPDIR="ivf_tl_Operate/bin/Release/net6.0-windows"
curl -s -X POST http://127.0.0.1:38080/shutdown -d 'token=tl13579'; echo # 受护栏停 control
sleep 3
# 仅改共享文件一处(模拟换机)
sed -i 's#key="mqttIp" value="192.168.0.108"#key="mqttIp" value="192.168.0.108"#' "$OPDIR/tl-shared.config"
grep mqttIp "$OPDIR/tl-shared.config"
powershell -Command "Start-Process -Verb RunAs -FilePath '$PWD/$OPDIR/control/ivf_tl_ControlHost.exe'"
sleep 12
curl -s http://127.0.0.1:38080/status; echo
Expected: control 正常起、/status started,证明改一处 tl-shared.config 即对 control 生效(operate 同机制读同文件 → 同样生效)。验证后把 mqttIp 改回 108。
沿用既往 E2E 脚手架口径(OPERATE_E2E=1 绕僵尸 Mutex、用 App.config 凭据走真实 AppDataInit)。确认 operate 经 file= 读到 urlIp 后真服务器登录成功 + 拉起 control。
Run(若僵尸 operate 仍占主 Mutex,用 E2E 包装;否则直接起):
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
OPDIR="ivf_tl_Operate/bin/Release/net6.0-windows"
# 提权启动真 operate.exe(它经 file= 读 tl-shared.config 的 urlIp → 登录 → 拉起 control)
powershell -Command "Start-Process -Verb RunAs -FilePath '$PWD/$OPDIR/ivf_tl_Operate.exe'"
sleep 20
curl -s http://127.0.0.1:38080/status; echo
Expected: operate 登录成功(不闪退)+ 10s 后拉起 control、/status started:true。证明 operate 端 file= 合并读取通。
若僵尸 PID 20268 占 operate 主 Mutex 致真 operate 起不来:记录"operate 外壳受僵尸门控、需重启清",但 Step2/3 已用真 control 进程坐实 file= 读取与单点生效;operate 与 control 读取机制同源(均 ConfigurationManager+file=),可据此判定。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0/ivf_tl_operate_2.0"
dotnet test ivf_tl_Operate.Tests/ivf_tl_Operate.Tests.csproj
dotnet test control/ivf_tl_SerialHelper.Tests/ivf_tl_SerialHelper.Tests.csproj
dotnet test control/IvfTl.Hardware.HilTests/IvfTl.Hardware.HilTests.csproj
dotnet build ivf_tl_Operate/ivf_tl_Operate.csproj -c Release
dotnet build control/ivf_tl_Control.sln -c Release
Expected: operate 4 绿;SerialHelper 40 绿;HIL 零写入 2 过 2 跳(无写开关);双编译 0 错。
Run:
curl -s -X POST http://127.0.0.1:38080/shutdown -d 'token=tl13579'; echo
sleep 3
curl -s http://127.0.0.1:38080/ping; echo " <- 应连不上=已退出"
Expected: /ping 连不上(control 干净退出、7 COM 释放)。
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0" && codegraph sync
Expected: 增量同步完成。
更新下列文档,与代码对齐后再提交:
项目文档/开发环境/连接配置清单-换服务器必读.md 第三节:operate/control 的 C# 换机由"改 operate+control 两份 App.config"改为"只改 operate 根的 tl-shared.config 一份";标注共享键来源。项目文档/开发环境/双进程部署指南.md:产物清单加 tl-shared.config(operate 根,control 经 ..\ 读);自检清单加"确认 tl-shared.config 在 operate 根、control\ 下无此文件";升级注意 PreserveNewest 保站点值。项目文档/需求文档/操作端逻辑与配置全景.md + control-逻辑与配置全景.md:连接键章节标注"共享键移入 tl-shared.config、operate 删 12 换气/CCD 死键"。续接三件套:进度状态.yaml(覆盖断点)、工作计划表.md(配置收敛状态)、交接卡.md(追加本次)、进度数据.js。
[ ] Step 9: 收尾提交
Run:
cd "C:/Users/AIVFO/Documents/trae_projects/TLProject/aivfo-tl-3.0"
git add 项目文档/
git commit -m "docs(config): 配置收敛文档同步——换机清单/部署指南/两全景/续接三件套
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
类型一致性核对: SharedConfigStore.Read(path,key) / Write(path,key,value)(Task2 定义)= AppConfigHelper 调用(Task3)= 测试调用(Task2)签名一致。✓