FilterPipeline::ordering_errors() in praxis-core hard-fails at startup on
misordered core filters, but it only checks core's own built-in filter
names — it has no visibility into filters defined in downstream crates
(e.g. ai's identity_header_guard, external_metering).
Concretely: external_metering depends on identity_header_guard running
first in the pipeline so identity ends up in filter_metadata before
metering reads it (see ai#709,
ai#581). If a deployment
config puts them in the wrong order today, nothing fails at startup — every
event just ships with token: 0 (or unmetered) silently, forever. This is
invisible to both tests and operators until someone notices the numbers are
wrong in production.
Proposal
Let filters declare their own ordering constraints (e.g. "must run after
X" / "must run before Y") through a small trait method or config
attribute, and have FilterPipeline's existing startup validation collect
constraints from every registered filter — core and downstream crates alike
— rather than special-casing a fixed list of core filter names.
Why not a filter-local workaround
A filter-local check (e.g. external_metering inspecting the configured
pipeline order at startup) would only catch this one pair, not the general
problem, and every future cross-crate dependency would need its own bespoke
check. A single pipeline-level mechanism instead:
- Fails closed at startup instead of degrading silently at runtime.
- Generalizes to every current and future cross-crate filter dependency.
- Keeps the enforcement in one place (
FilterPipeline) rather than
duplicated per filter.
Scope
- Design the constraint-declaration API (trait method vs. config attribute).
- Extend
ordering_errors() (or an equivalent build-time check) to consult
declared constraints from all registered filters, not just core's.
- Migrate
identity_header_guard / external_metering to declare their
existing implicit ordering requirement as the first real usage.
Follow-up from review discussion on
ai#581.
FilterPipeline::ordering_errors()inpraxis-corehard-fails at startup onmisordered core filters, but it only checks core's own built-in filter
names — it has no visibility into filters defined in downstream crates
(e.g.
ai'sidentity_header_guard,external_metering).Concretely:
external_meteringdepends onidentity_header_guardrunningfirst in the pipeline so identity ends up in
filter_metadatabeforemetering reads it (see ai#709,
ai#581). If a deployment
config puts them in the wrong order today, nothing fails at startup — every
event just ships with
token: 0(or unmetered) silently, forever. This isinvisible to both tests and operators until someone notices the numbers are
wrong in production.
Proposal
Let filters declare their own ordering constraints (e.g. "must run after
X" / "must run beforeY") through a small trait method or configattribute, and have
FilterPipeline's existing startup validation collectconstraints from every registered filter — core and downstream crates alike
— rather than special-casing a fixed list of core filter names.
Why not a filter-local workaround
A filter-local check (e.g.
external_meteringinspecting the configuredpipeline order at startup) would only catch this one pair, not the general
problem, and every future cross-crate dependency would need its own bespoke
check. A single pipeline-level mechanism instead:
FilterPipeline) rather thanduplicated per filter.
Scope
ordering_errors()(or an equivalent build-time check) to consultdeclared constraints from all registered filters, not just core's.
identity_header_guard/external_meteringto declare theirexisting implicit ordering requirement as the first real usage.
Follow-up from review discussion on
ai#581.