fix(permissions): assistant grants file is the one authority (live desync fix)

ai_grants_unified UNIONED the assistant grants.json with the legacy
settings/ai_permissions.json on every read. On archi-dev-box legacy held
all-ten and grants.json held four, so the Settings UI and the AIUI frame
saw every category ON while the assistant refused six — and no UI toggle
could fix it, because both write paths existed but only ai.permissions.set
synced both files. Now: an existing grants.json answers alone; the legacy
file is consulted only when no grants file exists (pre-unification
upgrade), and that read migrates forward and persists the authority.
assistant.grants-set now also rewrites the legacy file in step. Regression
tests: authority is not widened by legacy; migration folds forward once.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-07 07:12:37 -04:00
co-authored by Claude
parent 9bb38c36d3
commit 3a3ceb2238
3 changed files with 118 additions and 13 deletions
+11
View File
@@ -60,6 +60,8 @@ impl Grants {
/// data layer; `CallerScope::granted_categories` has no other source of
/// authority to fall back to.
pub async fn load(data_dir: &Path) -> Grants {
let path = data_dir.join(GRANTS_FILE);
let Ok(content) = tokio::fs::read_to_string(&path).await else {
return Grants::default_closed();
@@ -67,6 +69,15 @@ impl Grants {
serde_json::from_str(&content).unwrap_or_else(|_| Grants::default_closed())
}
/// Whether a grants file exists on disk at all. `load` cannot say this —
/// it maps "absent" and "present but empty" to the same value, and the
/// unified `ai.permissions.get` reader needs the distinction: an existing
/// file is authoritative, while an absent one triggers the one-time
/// legacy migration.
pub(crate) async fn exists(data_dir: &Path) -> bool {
tokio::fs::metadata(data_dir.join(GRANTS_FILE)).await.is_ok()
}
/// Persist the grants for this node, 0600 (following
/// `streaming/session.rs`'s `data_dir`-scoped persisted-state
/// convention, and this codebase's convention of keeping