test(agent/access): 固化自启动幂等边界
Rules-Applied: R01,R06,R07,R08,R10,R12,R13
This commit is contained in:
parent
17eb25c62e
commit
7c62a1c148
@ -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
|
||||
|
||||
@ -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。
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user