#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum AcpSurfaceOwner {
Editor,
Runtime,
}
#[derive(Debug, Clone, Copy)]
pub(super) struct AcpOwnedSurface {
pub name: &'static str,
pub owner: AcpSurfaceOwner,
pub rationale: &'static str,
}
pub(super) const ACP_OWNED_SURFACES: &[AcpOwnedSurface] = &[
AcpOwnedSurface {
name: "exec",
owner: AcpSurfaceOwner::Editor,
rationale: "Editor owns the terminal, approval UX, and undo for interactive \
command execution. ACP unregisters then re-registers this builtin.",
},
AcpOwnedSurface {
name: "shell",
owner: AcpSurfaceOwner::Editor,
rationale: "Same editor-owned terminal path as `exec`; also unregistered before \
re-registration rather than merely shadowed.",
},
AcpOwnedSurface {
name: "run_command",
owner: AcpSurfaceOwner::Editor,
rationale: "Hostlib-facing alias of editor-owned terminal execution over ACP.",
},
AcpOwnedSurface {
name: "host_call(process.exec)",
owner: AcpSurfaceOwner::Runtime,
rationale: "Direct host_call must pass harn command-policy/approval/sandbox \
preflight. It must not reach the editor first and skip gating.",
},
AcpOwnedSurface {
name: "host_call(process.spawn)",
owner: AcpSurfaceOwner::Runtime,
rationale: "Non-blocking sibling of process.exec; gated identically by canonical \
dispatch before any embedder bridge runs.",
},
AcpOwnedSurface {
name: "host_call(process.poll|wait|kill|release)",
owner: AcpSurfaceOwner::Runtime,
rationale: "Operate on harn's in-process spawn-handle registry. The editor has no \
such registry, so these cannot be served host-side.",
},
AcpOwnedSurface {
name: "host_call builtin fallbacks",
owner: AcpSurfaceOwner::Runtime,
rationale: "Canonical fallbacks (interaction.ask, project.metadata_*, workspace.*, \
template.render, process.list_shells, ...) run when the ACP bridge \
declines. They stay in harn-vm so standalone and ACP routes share one \
catalog; the editor may still answer first via host/call.",
},
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn terminal_builtins_are_editor_owned() {
for name in ["exec", "shell", "run_command"] {
let entry = ACP_OWNED_SURFACES
.iter()
.find(|surface| surface.name == name)
.unwrap_or_else(|| panic!("missing ownership row for {name}"));
assert_eq!(entry.owner, AcpSurfaceOwner::Editor, "{name}");
assert!(entry.rationale.len() > 40, "{name} needs a real rationale");
}
}
#[test]
fn process_host_call_surfaces_are_runtime_owned() {
for name in [
"host_call(process.exec)",
"host_call(process.spawn)",
"host_call(process.poll|wait|kill|release)",
"host_call builtin fallbacks",
] {
let entry = ACP_OWNED_SURFACES
.iter()
.find(|surface| surface.name == name)
.unwrap_or_else(|| panic!("missing ownership row for {name}"));
assert_eq!(entry.owner, AcpSurfaceOwner::Runtime, "{name}");
assert!(entry.rationale.len() > 40, "{name} needs a real rationale");
}
}
#[test]
fn ownership_table_has_no_duplicate_names() {
let mut seen = std::collections::BTreeSet::new();
for entry in ACP_OWNED_SURFACES {
assert!(
seen.insert(entry.name),
"duplicate ownership row for {}",
entry.name
);
}
}
}