feat: advance local-first workspace checklist

- add admin access-policy UI and local access control surfaces

- add local markdown conflict resolution UI and smoke coverage

- add ACP local agent changed-files audit scaffold and read-only write guard

- document current P0-P2 checklist progress and verification evidence
This commit is contained in:
lix-2026
2026-05-19 08:07:17 +08:00
parent a2cb1338c8
commit 68d321e297
36 changed files with 8643 additions and 235 deletions
@@ -707,6 +707,14 @@ pub(crate) fn ensure_write_contract(
)
.with_context(context));
}
if input.ai_access_scope_is_read_only() {
return Err(WebError::new(
axum::http::StatusCode::FORBIDDEN,
"mnote_tool_ai_scope_write_forbidden",
"当前 AI scope 是只读权限,禁止执行写入型 mnote tool",
)
.with_context(context));
}
Ok(())
}
@@ -1490,6 +1498,44 @@ fn build_insert_block(block_id: &str, value: &Value) -> Value {
mod tests {
use super::*;
#[test]
fn ensure_write_contract_rejects_read_only_ai_scope() {
let context = RequestContext::from_http_parts(
&axum::http::Method::POST,
&"/api/hermes/tools".parse().expect("uri"),
&axum::http::HeaderMap::new(),
);
let input = ToolCallInput {
tool_name: "mnote.block.replace".into(),
workspace_id: Some("ws_1".into()),
document_id: Some("doc_1".into()),
source_kind: Some("local_folder".into()),
root_uri: Some("file:///tmp/mnote-readonly".into()),
actor_id: Some("user_1".into()),
profile: None,
session_id: Some("sess_1".into()),
run_id: Some("run_1".into()),
tool_call_id: Some("tool_1".into()),
trace_id: Some("trace_1".into()),
idempotency_key: Some("idem_1".into()),
dry_run: Some(false),
capability_scope: None,
args: Some(json!({
"aiAccessScope": {
"permissionLevel": "read_only",
"allowedRoots": ["file:///tmp/mnote-readonly"]
}
})),
};
let error = ensure_write_contract(&context, &input).expect_err("read only rejected");
assert_eq!(error.status(), axum::http::StatusCode::FORBIDDEN);
assert_eq!(
error.message(),
"当前 AI scope 是只读权限,禁止执行写入型 mnote tool"
);
}
#[test]
fn content_to_text_reads_projection_content_nodes() {
let value = json!([