fix(agents): query shared instructions exactly

Issue one publisher-and-slug filter per assigned coordinate so unrelated cross-product notes cannot exhaust limited relay results.

Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz>

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-25 00:45:03 -07:00
committed by Rizz
parent 76df4bf1d1
commit af20831bb7
2 changed files with 107 additions and 40 deletions

View File

@@ -1,6 +1,6 @@
//! Progressive-disclosure metadata for owner-assigned relay skills.
//! Progressive-disclosure metadata for owner-assigned shared instructions.
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;
use nostr::{Event, Filter, Kind, PublicKey, SingleLetterTag};
@@ -46,23 +46,11 @@ pub(crate) async fn fetch_assigned_skill_covers(
if coordinates.is_empty() {
return Ok(None);
}
let authors = coordinates
.iter()
.map(|coordinate| coordinate.publisher)
.collect::<HashSet<_>>();
let slugs = coordinates
.iter()
.map(|coordinate| coordinate.slug.clone())
.collect::<HashSet<_>>();
let filter = Filter::new()
.kind(Kind::Custom(SKILL_KIND))
.authors(authors)
.custom_tags(SingleLetterTag::lowercase(nostr::Alphabet::D), slugs)
.limit(coordinates.len());
let filters = exact_coordinate_filters(&coordinates);
let value = rest
.query(&[filter])
.query(&filters)
.await
.map_err(|error| format!("relay skill query failed: {error}"))?;
.map_err(|error| format!("shared instruction query failed: {error}"))?;
let events = value
.as_array()
.ok_or_else(|| "relay skill query returned non-array".to_string())?
@@ -72,6 +60,20 @@ pub(crate) async fn fetch_assigned_skill_covers(
Ok(render_assigned_skill_covers(&coordinates, events))
}
fn exact_coordinate_filters(coordinates: &[Coordinate]) -> Vec<Filter> {
let d_tag = SingleLetterTag::lowercase(nostr::Alphabet::D);
coordinates
.iter()
.map(|coordinate| {
Filter::new()
.kind(Kind::Custom(SKILL_KIND))
.author(coordinate.publisher)
.custom_tags(d_tag, [coordinate.slug.as_str()])
.limit(1)
})
.collect()
}
fn render_assigned_skill_covers(coordinates: &[Coordinate], events: Vec<Event>) -> Option<String> {
let requested = coordinates
.iter()
@@ -152,6 +154,36 @@ mod tests {
use super::*;
use nostr::{EventBuilder, Keys, Tag, Timestamp};
#[test]
fn exact_query_filters_do_not_admit_author_slug_cross_products() {
let alpha = Keys::generate();
let beta = Keys::generate();
let coordinate =
|keys: &Keys, slug: &str| format!("30023:{}:{slug}", keys.public_key().to_hex());
let coordinates = [coordinate(&alpha, "alpha"), coordinate(&beta, "beta")]
.iter()
.filter_map(|value| parse_coordinate(value))
.collect::<Vec<_>>();
let filters = exact_coordinate_filters(&coordinates)
.into_iter()
.map(|filter| serde_json::to_value(filter).unwrap())
.collect::<Vec<_>>();
assert_eq!(filters.len(), 2);
assert_eq!(
filters[0]["authors"],
serde_json::json!([alpha.public_key().to_hex()])
);
assert_eq!(filters[0]["#d"], serde_json::json!(["alpha"]));
assert_eq!(filters[0]["limit"], serde_json::json!(1));
assert_eq!(
filters[1]["authors"],
serde_json::json!([beta.public_key().to_hex()])
);
assert_eq!(filters[1]["#d"], serde_json::json!(["beta"]));
}
#[test]
fn renders_only_verified_exact_assigned_covers_in_assignment_order() {
let first = Keys::generate();

View File

@@ -5,7 +5,7 @@
//! malformed publisher identities from becoming executable configuration.
use std::{
collections::{BTreeSet, HashMap, HashSet},
collections::{HashMap, HashSet},
time::Duration,
};
@@ -504,12 +504,11 @@ pub(crate) struct ResolvedRelaySkill {
pub editable: bool,
}
/// Fetch current NIP-23 heads for exact assigned coordinates in one relay REQ.
/// Fetch current NIP-23 heads for exact assigned coordinates.
///
/// The filter narrows by every requested author and slug, then the projection
/// checks each event against the requested `(publisher, d)` pair again. This
/// second check is the authorization boundary: a relay returning an event for
/// an unrequested cross-product coordinate cannot make it executable.
/// Each filter names one `(publisher, d)` pair. Keeping those constraints in
/// the same filter avoids the author × slug cross-product that can otherwise
/// let unrelated newer notes consume a limited result page.
pub(crate) async fn resolve_assigned_relay_skills(
relay_client: &NativeRelayClient,
relay_url: String,
@@ -525,25 +524,19 @@ pub(crate) async fn resolve_assigned_relay_skills(
.iter()
.map(|coordinate| parse_relay_skill_coordinate(coordinate))
.collect::<Result<Vec<_>, _>>()?;
let authors = parsed
.iter()
.map(|coordinate| coordinate.publisher.to_hex())
.collect::<BTreeSet<_>>();
let slugs = parsed
.iter()
.map(|coordinate| coordinate.slug.clone())
.collect::<BTreeSet<_>>();
let filter = serde_json::json!({
"kinds": [RELAY_SKILL_KIND],
"authors": authors,
"#d": slugs,
"limit": requested.len(),
});
let filters = exact_coordinate_filters(&parsed);
let session = relay_client.session(relay_url, keys).await;
let events = session
.fetch_events(filter, RELAY_SKILL_FETCH_TIMEOUT)
.await?;
let pages = futures_util::future::try_join_all(filters.into_iter().map(|filter| {
let session = &session;
async move {
session
.fetch_events(filter, RELAY_SKILL_FETCH_TIMEOUT)
.await
}
}))
.await?;
let events = pages.into_iter().flatten().collect();
let heads = tauri::async_runtime::spawn_blocking(move || {
resolved_heads_from_events(&requested, events)
})
@@ -552,6 +545,20 @@ pub(crate) async fn resolve_assigned_relay_skills(
Ok(heads)
}
fn exact_coordinate_filters(coordinates: &[RelaySkillCoordinate]) -> Vec<serde_json::Value> {
coordinates
.iter()
.map(|coordinate| {
serde_json::json!({
"kinds": [RELAY_SKILL_KIND],
"authors": [coordinate.publisher.to_hex()],
"#d": [&coordinate.slug],
"limit": 1,
})
})
.collect()
}
fn resolved_heads_from_events(
coordinates: &[String],
events: Vec<Event>,
@@ -753,6 +760,34 @@ mod tests {
assert_eq!(reasons[0].code, RelaySkillIncompatibilityCode::EmptyBody);
}
#[test]
fn exact_query_filters_do_not_admit_author_slug_cross_products() {
let alpha = Keys::generate();
let beta = Keys::generate();
let coordinates = [
format!("30023:{}:alpha", alpha.public_key().to_hex()),
format!("30023:{}:beta", beta.public_key().to_hex()),
]
.iter()
.map(|value| parse_relay_skill_coordinate(value).unwrap())
.collect::<Vec<_>>();
let filters = exact_coordinate_filters(&coordinates);
assert_eq!(filters.len(), 2);
assert_eq!(
filters[0]["authors"],
serde_json::json!([alpha.public_key().to_hex()])
);
assert_eq!(filters[0]["#d"], serde_json::json!(["alpha"]));
assert_eq!(filters[0]["limit"], serde_json::json!(1));
assert_eq!(
filters[1]["authors"],
serde_json::json!([beta.public_key().to_hex()])
);
assert_eq!(filters[1]["#d"], serde_json::json!(["beta"]));
}
#[test]
fn resolves_only_exact_requested_signer_and_slug() {
let alpha = Keys::generate();