knowledge: per-persona reviewed-only mode + agent reads include reviewed

Two coupled changes to the agent's recall surface:

1. Default scope expanded. recall_facts_for_photo and recall_entities
   used to filter to status='active' only — which silently dropped
   'reviewed' (human-verified) facts. Now they surface active +
   reviewed by default. Reviewed is strictly more trusted than
   active and shouldn't have been hidden. Rejected and superseded
   stay filtered.

2. New persona toggle `reviewed_only_facts` (BOOLEAN, default false,
   migration 2026-05-10-000400). When set, the agent's recall on
   that persona returns ONLY facts with status='reviewed' — strict
   mode for tasks where hallucinated agent claims are particularly
   costly. Wired:
   - schema.rs / Persona / InsertPersona / PersonaPatch grow the
     field.
   - PersonaView returns it as `reviewedOnlyFacts` (camelCase wire).
   - PUT /personas/{id} accepts it (mobile editor surfaces it).
   - InsightGenerator now carries a PersonaDao reference so
     recall_facts_for_photo can read the active persona's flag at
     start; one extra read per recall, cheap.

Composes with include_all_memories: that operates on the persona
*scope* axis (single vs hive), reviewed_only_facts on the *status*
axis. They're orthogonal.

Legacy persona rows pick up the default false on migration; no
behavior change unless explicitly toggled. The 4 existing persona
construction sites (one production, two tests, one InsertPersona in
knowledge_dao tests) all default the field. populate_knowledge bin
+ state.rs constructors also wire the new persona_dao arg.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cameron Cordes
2026-05-10 20:21:39 -04:00
parent f53338923d
commit 86c331571d
10 changed files with 110 additions and 4 deletions

View File

@@ -35,6 +35,11 @@ pub struct PersonaView {
pub created_at: i64,
#[serde(rename = "updatedAt")]
pub updated_at: i64,
/// "Strict mode" — when true, the agent's recall_* tools return
/// only facts whose status is 'reviewed'. See migration
/// 2026-05-10-000400.
#[serde(rename = "reviewedOnlyFacts")]
pub reviewed_only_facts: bool,
}
impl From<Persona> for PersonaView {
@@ -47,6 +52,7 @@ impl From<Persona> for PersonaView {
include_all_memories: p.include_all_memories,
created_at: p.created_at,
updated_at: p.updated_at,
reviewed_only_facts: p.reviewed_only_facts,
}
}
}
@@ -72,6 +78,8 @@ pub struct UpdatePersonaRequest {
pub system_prompt: Option<String>,
#[serde(default, rename = "includeAllMemories")]
pub include_all_memories: Option<bool>,
#[serde(default, rename = "reviewedOnlyFacts")]
pub reviewed_only_facts: Option<bool>,
}
#[derive(Deserialize)]
@@ -249,6 +257,7 @@ async fn update_persona(
name: body.name.clone(),
system_prompt: body.system_prompt.clone(),
include_all_memories: body.include_all_memories,
reviewed_only_facts: body.reviewed_only_facts,
};
match dao.update_persona(&cx, uid, &pid, patch) {
Ok(Some(p)) => HttpResponse::Ok().json(PersonaView::from(p)),