Skip to content

SpecMatch (fits) filters always evaluate false in the Python-facing filter API #4

Description

@jscott3201

Problem

FilterNode::SpecMatch is correctly wired to DefNamespace::fits inside haystack-core's evaluator, but two of the three public entry points that evaluate a filter never thread a namespace through, so fits ph::Ahu silently evaluates to false there regardless of what's registered in the namespace.

haystack-core/src/filter/eval.rs:

pub fn matches<'a>(node: &FilterNode, entity: &'a HDict, resolve_ref: ResolveRef<'a>) -> bool {
    matches_with_ns(node, entity, resolve_ref, None)
}
...
FilterNode::SpecMatch(spec) => {
    match namespace {
        Some(ns) => { ... ns.fits(entity, &type_name) }
        None => false,
    }
}

matches() hardcodes namespace: None, so any caller that only has the 3-arg matches() gets SpecMatch == false unconditionally.

That 2-arg matches() is exactly what the two evaluation-only surfaces call:

  • rusty-haystack/src/filter.rs (Python bindings), PyFilter::matches and the module-level matches_filter():
fn matches(&self, entity: &PyHDict) -> bool {
    filter::matches(&self.inner, &entity.inner, None)
}
...
pub fn matches_filter(filter_expr: &str, entity: &PyHDict) -> PyResult<bool> {
    ...
    Ok(filter::matches(&node, &entity.inner, None))
}

Neither takes a namespace argument, and there is no alternate Python-facing function that does. EntityGraph::equip_points() in haystack-core/src/graph/entity_graph.rs has the same bug — its optional filter argument is evaluated with crate::filter::matches(&ast, e, None) even though self.namespace is available on the struct, while EntityGraph's main query path correctly uses matches_with_ns(&ast, entity, Some(&resolver), ns).

Impact

Any downstream code that evaluates a standalone Filter/matches_filter() from Python (outside of EntityGraph::query), or that calls EntityGraph::equip_points(equip_ref, Some("fits ...")), cannot use fits <spec> filter syntax at all — it always returns no matches. Verdant must reimplement structural-fit filtering by calling DefNamespace.fits()/fits_explain() directly in application code instead of composing it into filter expressions, which defeats the purpose of having SpecMatch in the filter grammar.

Proposed direction

  • Add a namespace-aware overload to the Python bindings (e.g. PyFilter.matches_ns(entity, namespace) and a matches_filter_ns(expr, entity, namespace) module function) that calls matches_with_ns instead of matches.
  • Fix EntityGraph::equip_points() to call matches_with_ns(&ast, e, None, self.namespace.as_ref()) instead of the namespace-less matches(), matching the behavior of the main query path.

References

Project Haystack filter grammar — fits spec-match term (Haystack 4 / Xeto structural typing).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions