test(agent/access): 固化自启动幂等边界
All checks were successful
Push & PR Check / check (push) Successful in 57s
Push & PR Check / check (pull_request) Successful in 56s

Rules-Applied: R01,R06,R07,R08,R10,R12,R13
This commit is contained in:
lanrtop 2026-09-23 03:38:12 +09:00
parent 17eb25c62e
commit 7c62a1c148
3 changed files with 59 additions and 11 deletions

View File

@ -126,8 +126,8 @@ Codex / Claude 项目
- depends: A2. 分层 Agent runtime readiness 本体
- files: src-tauri/Cargo.toml, src-tauri/Cargo.lock, src-tauri/src/lib.rs, src-tauri/src/commands/settings.rs
- acceptance: "[Tauri command] set_agent_autostart(enabled) → 默认 false,可启用、查询并撤销;重复调用幂等;不启动第二个炼境实例,不修改项目文件或 pnpm-lock.yaml。"
- decision_note: "用户于 2026-09-23 明确恢复 A6,但限定正式安装版:默认关闭,debug/target 构建拒绝启用,查询/启停幂等,复用 single-instance;updater 单独建设。"
- done_note: "接入官方 tauri-plugin-autostart,保留既有 single-instance;query/set command 对重复调用幂等,debug 与 target/release 路径拒绝启用,未改 pnpm-lock。路径 fixture、cargo test --lib 178/178 与 cargo check --release 通过;`mise exec -- pnpm build:local` 已生成 NSIS/MSI,NSIS SHA-256=82F05ECB0A68E5B87FCD5D50906A64A4CD07B6E9169A07CB2F20D6130FE36C12。正式安装后的注册表效果留待安装包 human_verify。"
- decision_note: "用户于 2026-09-23 明确恢复 A6,但限定正式安装版:默认关闭,debug/target 构建拒绝启用,查询/启停幂等,复用 single-instance;开发前先退出正式版,不并行运行两个实例;updater 单独建设。"
- done_note: "接入官方 tauri-plugin-autostart,保留既有 single-instance;query/set command 通过纯状态机 fixture 证明重复调用幂等,debug 与 target/release 路径拒绝启用,未改 pnpm-lock。路径 fixture、cargo test --lib 179/179 与 cargo check --release 通过;`mise exec -- pnpm build:local` 已生成 NSIS/MSI,NSIS SHA-256=82F05ECB0A68E5B87FCD5D50906A64A4CD07B6E9169A07CB2F20D6130FE36C12。正式安装后的注册表效果留待安装包 human_verify。"
### ✅ A7. Agent 连接状态与自启动 UI 投影
- status: done

View File

@ -133,6 +133,7 @@ acceptance 是 agent 自验的唯一判断依据,必须是可执行断言:
- readiness 分为 `configured → reachable → handshake → tools_ready → reload_required`。配置存在不等于当前任务已加载;停在哪层,就只修哪一层。
- `.lian-jing/context.json` 是项目身份唯一来源;Skill 和插件不复制项目身份、Gitea 凭证或 MCP 实现。
- Windows 登录自启动只服务正式安装版,默认关闭且由用户主动启用;debug 和 `target/release` 直接运行明确拒绝注册。single-instance 继续保证同一时间只有一个炼境实例。
- 开发炼境前先从托盘完全退出正式安装版;不得把正式版与 `pnpm tauri dev` 并行运行。single-instance 是运行时兜底,不替代这一开发约定。
- updater 是独立的发布能力,不属于自启动或 readiness 的完成条件。
这套 scope 是单用户本机的误操作隔离,不是多租户认证。威胁模型变化前,不向项目配置写持久 token。

View File

@ -604,6 +604,28 @@ pub struct AgentAutostartStatus {
pub detail: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AutostartAction {
RefuseEnable,
Noop,
Enable,
Disable,
}
fn plan_autostart_action(eligible: bool, current: bool, requested: bool) -> AutostartAction {
if !eligible && requested {
return AutostartAction::RefuseEnable;
}
if !eligible || current == requested {
return AutostartAction::Noop;
}
if requested {
AutostartAction::Enable
} else {
AutostartAction::Disable
}
}
fn autostart_eligibility(is_debug: bool, executable: &Path) -> (bool, String, String) {
let executable_text = executable.to_string_lossy().to_string();
if !cfg!(target_os = "windows") {
@ -688,18 +710,15 @@ pub fn set_agent_autostart(
enabled: bool,
) -> Result<AgentAutostartStatus, String> {
let before = get_agent_autostart_status(app.clone())?;
if !before.eligible {
if enabled {
return Err(before.detail);
}
return Ok(before);
}
if before.enabled != enabled {
if enabled {
match plan_autostart_action(before.eligible, before.enabled, enabled) {
AutostartAction::RefuseEnable => return Err(before.detail),
AutostartAction::Noop => return Ok(before),
AutostartAction::Enable => {
app.autolaunch()
.enable()
.map_err(|error| error.to_string())?;
} else {
}
AutostartAction::Disable => {
app.autolaunch()
.disable()
.map_err(|error| error.to_string())?;
@ -1331,4 +1350,32 @@ mod tests {
assert_eq!(kind, "installed_release");
assert!(detail.contains("正式安装版"));
}
#[test]
fn autostart_transition_is_idempotent_and_refuses_ineligible_enable() {
assert_eq!(
plan_autostart_action(false, false, true),
AutostartAction::RefuseEnable
);
assert_eq!(
plan_autostart_action(false, false, false),
AutostartAction::Noop
);
assert_eq!(
plan_autostart_action(true, false, false),
AutostartAction::Noop
);
assert_eq!(
plan_autostart_action(true, true, true),
AutostartAction::Noop
);
assert_eq!(
plan_autostart_action(true, false, true),
AutostartAction::Enable
);
assert_eq!(
plan_autostart_action(true, true, false),
AutostartAction::Disable
);
}
}