/// store/store_rsv.mbt —— 作用域预订(拿来主义:Interlinked 文件预订 → 多 agent 并发编辑冲突预防)
///
/// 让多个 AI 客户端(并发 agent)在同一命名空间/工作区协作时,可对文件/目录/资源"作用域"
/// 做轻量预订:预订在 TTL 内独占,其他 agent `reserve_check` 会被告知持有者,避免同时改同一文件。
/// 双后端(内存 + SQLite)一致,供 server 工具调用。
///|
/// 内存后端:写入/覆盖某作用域预订。(返回值约定:Ok(msg),不使用)
pub fn MemoryStore::rsv_set(
self : MemoryStore,
scope~ : String,
agent~ : String,
ttl_until~ : String,
created_at~ : String,
) -> Result[String, String] {
self.reservations.set(scope, (agent, ttl_until, created_at))
Ok("reserved")
}
///|
/// 内存后端:查询某作用域预订(未预订返回 None)。
pub fn MemoryStore::rsv_get(
self : MemoryStore,
scope : String,
) -> (String, String, String)? {
self.reservations.get(scope)
}
///|
/// 内存后端:释放预订(仅持有者可释放;不存在或非持有者返回 false)。
pub fn MemoryStore::rsv_release(
self : MemoryStore,
scope : String,
agent : String,
) -> Bool {
match self.reservations.get(scope) {
Some((a, _, _)) if a == agent => {
self.reservations.remove(scope)
true
}
_ => false
}
}
///|
/// 内存后端:列出全部预订(升序,无指定顺序保证)。
pub fn MemoryStore::rsv_list(
self : MemoryStore,
) -> Array[(String, String, String)] {
let out : Array[(String, String, String)] = []
for (_, v) in self.reservations {
out.push(v)
}
out
}
// —— BUG-136:跨进程裁决的预订(rsv_try_*)——
///|
/// 预订裁决结果。`agent`/`ttl_until` 一律是**库里现值**(conflict 时即 held_by),
/// 不是调用方想要的那个值——回执不许由请求反推。
pub struct RsvOutcome {
reserved : Bool
/// reserved | renewed | taken_over | conflict
action : String
/// 仅 taken_over 非空:被让渡掉的原持有者
prev_agent : String
agent : String
ttl_until : String
}
///|
/// 裁决规则**单一真源**:给定写前快照与本次请求,判「该不该写」+动作标签。
/// 两个后端都从这里取标签,SQLite 侧的 `ON CONFLICT ... WHERE` 编码的是同一条规则
/// (`reservations.agent = excluded.agent OR reservations.ttl_until < excluded.created_at`)。
/// 同持有者排在过期之前:自己续一条已过期的预订,报 renewed 而不是 taken_over(与旧口径逐字一致)。
pub fn rsv_action(
prev : (String, String, String)?,
agent : String,
now : String,
) -> (Bool, String) {
match prev {
None => (true, "reserved")
Some((owner, _, _)) if owner == agent => (true, "renewed")
Some((_, ttl, _)) if ttl < now => (true, "taken_over")
_ => (false, "conflict")
}
}
///|
/// 内存后端:按裁决写预订,**写后回读**收口。
/// 旧形状(先 rsv_get 判空再无条件 rsv_set)在跨进程下会双方都读到空闲、双方都写成功;
/// 内存后端本来没有那个窗口,但两后端必须同一形状,否则单测永远绿而 SQLite 红(BUG-85/BUG-79 的旧例)。
pub fn MemoryStore::rsv_try_set(
self : MemoryStore,
scope~ : String,
agent~ : String,
ttl_until~ : String,
created_at~ : String,
) -> Result[RsvOutcome, String] {
let prev = self.rsv_get(scope)
let (may_write, action) = rsv_action(prev, agent, created_at)
let prev_agent = match prev {
Some((o, _, _)) if action == "taken_over" => o
_ => ""
}
if may_write {
ignore(self.rsv_set(scope~, agent~, ttl_until~, created_at~))
}
// 结论以库里的现值为准:内存后端在这里必然与请求一致,写成回读是为了两后端同构
match self.rsv_get(scope) {
Some((owner, ttl, _)) if owner == agent =>
Ok(RsvOutcome::{
reserved: true,
action,
prev_agent,
agent,
ttl_until: ttl,
})
Some((owner, ttl, _)) =>
Ok(RsvOutcome::{
reserved: false,
action: "conflict",
prev_agent: "",
agent: owner,
ttl_until: ttl,
})
None =>
Ok(RsvOutcome::{
reserved: false,
action: "conflict",
prev_agent: "",
agent: "",
ttl_until: "",
})
}
}
// —— 执行者能力注册(Marketplace 雏形跨进程持久化,R31)——
///|
/// 内存后端:写入/覆盖一个执行者的能力注册(幂等)。
pub fn MemoryStore::executor_save(
self : MemoryStore,
name~ : String,
abilities_json~ : String,
created_at~ : String,
) -> Result[String, String] {
self.executors.set(name, (abilities_json, created_at))
Ok("saved")
}
///|
/// 内存后端:列出全部执行者 (name, abilities_json, created_at)。
pub fn MemoryStore::executor_list(
self : MemoryStore,
) -> Array[(String, String, String)] {
let out : Array[(String, String, String)] = []
for (k, v) in self.executors {
out.push((k, v.0, v.1))
}
out
}
///|
/// 内存后端:清空全部执行者能力注册(Marketplace 重置/整洁)。
pub fn MemoryStore::executor_clear(self : MemoryStore) -> Bool {
self.executors.clear()
true
}