Problem
The two decision-carrying events reorder the same fields:
// sla_calc (src/lib.rs, publish_sla_event)
(outage_id, status, payment_type, rating, mttr_minutes, threshold_minutes, amount)
// dup_input (src/lib.rs, publish_duplicate_input_event)
(outage_id, status, mttr_minutes, threshold_minutes, amount, payment_type, rating, config_version_hash, recorded_at)
Both are documented in src/event_schema.rs with these divergent layouts.
Consequences:
- Indexers maintain two decoders: the
dup_input event — emitted specifically so consumers can reconcile a rejection "without a second get_latest_by_outage read" — encodes payment_type/rating after amount, while sla_calc puts them before; a parser written from one schema misreads the other.
- Field-order drift is invisible to the stability guardrail:
event_name_symbols() in src/api_stability.rs checks names only, so reordering either payload passes CI (the guardrail cannot detect payload-shape drift at all).
- The "append-only, additive changes are safe" policy breaks down here:
dup_input mirrors SLAResult's struct order while sla_calc uses a semantic order — the two events cannot be merged by a consumer without per-event logic.
Root cause
sla_calc predates SLAResult's 9-field struct order; dup_input was later written to mirror the struct, and no canonical payload-order rule was enforced across events.
Why this is architecturally hard
- Reordering either payload is a breaking ABI change; the realistic fix is to define a canonical field order (e.g. the
SLAResult struct order) and either migrate sla_calc to it with a version bump or document a single shared layout going forward.
- The SC-099 checklist and
docs/EVENT_DRIFT_CHECKLIST.md guide reviews but do not mechanically compare payload layouts across events; enforcing a shared order needs a test that asserts the tuple shapes (or generated event-fixture snapshots).
dup_input deliberately carries two extra fields (config_version_hash, recorded_at); the canonical order must accommodate the full SLAResult shape so all three events align.
Acceptance criteria
Out of scope
Adding the missing fields to set_int (companion issue) and payload-size budget enforcement.
Getting started
Good first files to read: apexchainx_calculator/src/lib.rs (the three publish_* helpers), apexchainx_calculator/src/event_schema.rs.
Problem
The two decision-carrying events reorder the same fields:
Both are documented in
src/event_schema.rswith these divergent layouts.Consequences:
dup_inputevent — emitted specifically so consumers can reconcile a rejection "without a secondget_latest_by_outageread" — encodespayment_type/ratingafteramount, whilesla_calcputs them before; a parser written from one schema misreads the other.event_name_symbols()insrc/api_stability.rschecks names only, so reordering either payload passes CI (the guardrail cannot detect payload-shape drift at all).dup_inputmirrorsSLAResult's struct order whilesla_calcuses a semantic order — the two events cannot be merged by a consumer without per-event logic.Root cause
sla_calcpredatesSLAResult's 9-field struct order;dup_inputwas later written to mirror the struct, and no canonical payload-order rule was enforced across events.Why this is architecturally hard
SLAResultstruct order) and either migratesla_calcto it with a version bump or document a single shared layout going forward.docs/EVENT_DRIFT_CHECKLIST.mdguide reviews but do not mechanically compare payload layouts across events; enforcing a shared order needs a test that asserts the tuple shapes (or generated event-fixture snapshots).dup_inputdeliberately carries two extra fields (config_version_hash,recorded_at); the canonical order must accommodate the fullSLAResultshape so all three events align.Acceptance criteria
sla_calc,set_int, anddup_input.Out of scope
Adding the missing fields to
set_int(companion issue) and payload-size budget enforcement.Getting started
just testGood first files to read:
apexchainx_calculator/src/lib.rs(the threepublish_*helpers),apexchainx_calculator/src/event_schema.rs.