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).
Problem
FilterNode::SpecMatchis correctly wired toDefNamespace::fitsinsidehaystack-core's evaluator, but two of the three public entry points that evaluate a filter never thread a namespace through, sofits ph::Ahusilently evaluates tofalsethere regardless of what's registered in the namespace.haystack-core/src/filter/eval.rs:matches()hardcodesnamespace: None, so any caller that only has the 3-argmatches()getsSpecMatch==falseunconditionally.That 2-arg
matches()is exactly what the two evaluation-only surfaces call:rusty-haystack/src/filter.rs(Python bindings),PyFilter::matchesand the module-levelmatches_filter():Neither takes a namespace argument, and there is no alternate Python-facing function that does.
EntityGraph::equip_points()inhaystack-core/src/graph/entity_graph.rshas the same bug — its optionalfilterargument is evaluated withcrate::filter::matches(&ast, e, None)even thoughself.namespaceis available on the struct, whileEntityGraph's main query path correctly usesmatches_with_ns(&ast, entity, Some(&resolver), ns).Impact
Any downstream code that evaluates a standalone
Filter/matches_filter()from Python (outside ofEntityGraph::query), or that callsEntityGraph::equip_points(equip_ref, Some("fits ...")), cannot usefits <spec>filter syntax at all — it always returns no matches. Verdant must reimplement structural-fit filtering by callingDefNamespace.fits()/fits_explain()directly in application code instead of composing it into filter expressions, which defeats the purpose of havingSpecMatchin the filter grammar.Proposed direction
PyFilter.matches_ns(entity, namespace)and amatches_filter_ns(expr, entity, namespace)module function) that callsmatches_with_nsinstead ofmatches.EntityGraph::equip_points()to callmatches_with_ns(&ast, e, None, self.namespace.as_ref())instead of the namespace-lessmatches(), matching the behavior of the main query path.References
Project Haystack filter grammar —
fitsspec-match term (Haystack 4 / Xeto structural typing).