{"record":{"id":"08fff8a7d5bea8d0","repo":"astral-sh/ruff","slug":"every-tdd-constraint-should-have-a-source-order","errorCode":null,"errorMessage":"every TDD constraint should have a source order","messagePattern":"every TDD constraint should have a source order","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/types/constraints.rs","lineNumber":3744,"sourceCode":"\nimpl Debug for NodeId {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut f = f.debug_tuple(\"Node\");\n        match self.node() {\n            // We use format_args instead of rendering the strings directly so that we don't get\n            // any quotes in the output: ScopedReachabilityConstraintId(AlwaysTrue) instead of\n            // ScopedReachabilityConstraintId(\"AlwaysTrue\").\n            Node::AlwaysTrue => f.field(&format_args!(\"AlwaysTrue\")),\n            Node::AlwaysFalse => f.field(&format_args!(\"AlwaysFalse\")),\n            Node::Interior(_) => f.field(&self.0),\n        };\n        f.finish()\n    }\n}\n\nimpl std::ops::Add<usize> for NodeId {\n    type Output = NodeId;\n\n    fn add(self, rhs: usize) -> Self::Output {\n        NodeId::from_usize(self.index() + rhs)\n    }\n}\n\nimpl Idx for NodeId {\n    #[inline]\n    fn new(value: usize) -> Self {\n        Self::from_usize(value)\n    }\n\n    #[inline]\n    fn index(self) -> usize {\n        debug_assert!(!self.is_terminal());\n        self.0 as usize\n    }\n}\n","sourceCodeStart":3726,"sourceCodeEnd":3762,"githubUrl":"https://github.com/astral-sh/ruff/blob/15f3fe6b15a5f00172f34b0f542f8ea277f5a586/crates/ty_python_semantic/src/types/constraints.rs#L3726-L3762","documentation":"`PathBounds::compute` walks the BDD's satisfying paths and sorts each path's constraints by their position in a registered source-order set (`source_orders: FxIndexSet<ConstraintId>`). The expect asserts every positive constraint encountered on a path has a registered source order; it fires when the path walker visits constraints the registration set does not contain.","triggerScenarios":"Computing per-path typevar bounds (assignability checks, bound narrowing) where a constraint on a path was never inserted into `self.source_orders` - e.g. constraints added during folding without registration, or a source-order set rebuilt/compacted differently than the path walker expects.","commonSituations":"Contributions to constraint ordering/determinism machinery (new constraint producers, compaction changes); surfaces in ordering-sensitive mdtests and the constraint-order wobble tests rather than normal checks.","solutions":["Register every constraint in `source_orders` at the single point where it is added to the storage the walker will traverse.","If registration can legitimately miss, return a deterministic fallback order (insert on miss) behind a debug assertion and investigate the miss.","Add a regression mdtest with the constraint shape that panicked and run `cargo nextest run -p ty_python_semantic --test mdtest`.","Report upstream with the fixture if stock ty panics."],"exampleFix":"// before: constraint added to storage only\nlet constraint = storage.add_constraint(db, c);\n\n// after: keep path ordering registration in lockstep\nlet constraint = storage.add_constraint(db, c);\nsource_orders.insert(constraint);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let bounds = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    PathBounds::compute(db, env, storage, node, inferable, source_order)\n}))\n.unwrap_or_else(|_| PathBounds::Unconstrained); // conservative fallback","preventionTips":["Contributors: register constraints in source_orders at insert time; the walker assumes total registration.","Run the constraint-order wobble tests when touching ordering/determinism code.","Users: capture the backtrace and constraint-heavy reproducer before reporting."],"tags":["rust","panic","expect","ty","constraints","tdd","source-order","determinism"],"backgroundTag":"constraint-solver-invariant-panic","analyzedSha":"15f3fe6b15a5f00172f34b0f542f8ea277f5a586","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}