Feature chore bug 005 #19
@@ -0,0 +1,96 @@
|
||||
//! Pure scope-control logic (FR-MCP-3), split out from `mcp/mod.rs` so it's
|
||||
//! unit-testable without a DB, a running server, or the `mcp` cargo feature.
|
||||
//!
|
||||
//! Design note (documented here because the schema doesn't (yet) carry a
|
||||
//! per-meeting "expose this meeting" flag -- only `feature_briefs.exposed`
|
||||
//! does, per `docs/03-data-model.md`): with `ExposeScope::Selected`, meetings/
|
||||
//! transcripts/action-items have no selection mechanism to key off in this
|
||||
//! milestone, so they are treated the same as `None` (deny) rather than the
|
||||
//! same as `All` (allow) -- a privacy-conservative default consistent with
|
||||
//! every other WA default (recording/sync/hosted-AI/MCP itself all default
|
||||
//! OFF). Only `get_feature_brief` has real per-item selection today, via the
|
||||
//! brief's own `exposed` flag (M1). A future "select meetings" UI/schema
|
||||
//! addition should upgrade `Selected` for the other three tools without
|
||||
//! changing this function's callers.
|
||||
|
||||
use crate::mcp::ExposeScope;
|
||||
|
||||
/// Whether `list_recent_meetings`/`get_transcript`/`get_action_items` may see
|
||||
/// meetings at all under the current scope. `Selected` has no per-meeting
|
||||
/// selection mechanism yet (see module docs) so it is conservatively treated
|
||||
/// like `None`.
|
||||
pub fn meetings_visible(scope: ExposeScope) -> bool {
|
||||
matches!(scope, ExposeScope::All)
|
||||
}
|
||||
|
||||
/// Whether a specific feature brief may be served. `exposed` is the brief's
|
||||
/// own per-item flag (`feature_briefs.exposed`, set via `set_brief_exposed`).
|
||||
pub fn brief_visible(scope: ExposeScope, exposed: bool) -> bool {
|
||||
match scope {
|
||||
ExposeScope::None => false,
|
||||
ExposeScope::Selected => exposed,
|
||||
ExposeScope::All => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Recordings (`.wav`) are never exposed unless explicitly allowed (FR-MCP-3),
|
||||
/// independent of `ExposeScope`. None of the four MCP tools serve raw audio
|
||||
/// bytes today, but a meeting that retained its recording (ADR-0009) is
|
||||
/// treated as more sensitive-by-association: its transcript/action items are
|
||||
/// also withheld unless the user opted into `expose_recordings`. Every tool
|
||||
/// handler must call this for each candidate meeting -- there is no central
|
||||
/// choke point (FR-MCP-3 "enforce in every tool handler").
|
||||
pub fn recording_gate_ok(expose_recordings: bool, meeting_recorded: bool) -> bool {
|
||||
expose_recordings || !meeting_recorded
|
||||
}
|
||||
|
||||
/// Combined check a tool handler runs before including one meeting's data.
|
||||
pub fn meeting_allowed(scope: ExposeScope, expose_recordings: bool, meeting_recorded: bool) -> bool {
|
||||
meetings_visible(scope) && recording_gate_ok(expose_recordings, meeting_recorded)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn none_hides_all_meetings() {
|
||||
assert!(!meetings_visible(ExposeScope::None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selected_hides_meetings_pending_a_selection_mechanism() {
|
||||
// Documented conservative choice -- see module docs.
|
||||
assert!(!meetings_visible(ExposeScope::Selected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_shows_meetings() {
|
||||
assert!(meetings_visible(ExposeScope::All));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brief_visibility_follows_the_exposed_flag_only_under_selected() {
|
||||
assert!(!brief_visible(ExposeScope::None, true));
|
||||
assert!(!brief_visible(ExposeScope::Selected, false));
|
||||
assert!(brief_visible(ExposeScope::Selected, true));
|
||||
assert!(brief_visible(ExposeScope::All, false));
|
||||
assert!(brief_visible(ExposeScope::All, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recordings_never_served_unless_explicitly_allowed() {
|
||||
assert!(!recording_gate_ok(false, true));
|
||||
assert!(recording_gate_ok(false, false));
|
||||
assert!(recording_gate_ok(true, true));
|
||||
assert!(recording_gate_ok(true, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meeting_allowed_requires_both_scope_and_recording_gate() {
|
||||
assert!(!meeting_allowed(ExposeScope::All, false, true)); // recorded, not opted-in
|
||||
assert!(meeting_allowed(ExposeScope::All, false, false)); // not recorded
|
||||
assert!(meeting_allowed(ExposeScope::All, true, true)); // opted-in
|
||||
assert!(!meeting_allowed(ExposeScope::None, true, false)); // scope still wins
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user