conditional-edge-testing-941d73 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited conditional-edge-testing-941d73 (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Any test that constructs a BranchDefinition with a non-empty conditional_edges list MUST also exercise the runtime path. Shape:
workflow.graph_compiler.compile_branch(...) with ascripted provider_call that seeds the gate's output_keys[0].
compiled.graph.compile().invoke(initial_state).populated, and the non-selected-path's output_key did not.
Validation (branch.validate()) + serialization round-trip + storage tests are NECESSARY but NOT SUFFICIENT. The Tier-1 investigation (2026-04-24, task #7) surfaced a router/LangGraph contract inversion that survived months because no test compiled+invoked a conditional_edges branch end-to-end.
LangGraph's add_conditional_edges(source, router, path_map) is a two-sided contract:
router callable returns a KEY.path_map (dict) looks that key up → target node.If either side diverges from the other (e.g., router returns a target name instead of a key), graph.invoke raises KeyError deep in LangGraph. That error never surfaces in validation/storage tests because those never drive the graph.
tests/test_conditional_routing_resolver.py (new in #7).
TestHappyCaseRouting — gate emits "A" → path_a runs; gate emits"B" → path_b runs.
TestSymptom1LiteralEndConditions — conditions={"STOP": "END"}normalizes the literal "END" string → LangGraph's END sentinel.
TestSymptom2TerminalNoopAndLoopBack — gate invoked twice readsfresh state each call (not closure-captured).
TestSymptom3ThreeGateIterations — 3 distinct gate outputs eachroute distinctly (not compile-time mapping capture).
tests/test_conditional_edges_compile_invoke.py::TestContractInversionRegressionGuard keeps a hand-built pre-fix router (returns a target, not a label) and asserts graph.invoke raises KeyError. If someone reverts the fix in workflow/graph_compiler.py:_build_conditional_router, that test goes red immediately.
Any test exercising a conditional-edge branch needs a provider that returns the gate's decision value. Minimal pattern:
def _scripted_provider(gate_output: str, *, gate_marker: str = "decide"):
def _call(prompt, system="", *, role="writer"):
if gate_marker in prompt:
return gate_output
return "leaf ran"
return _callPass as compile_branch(branch, provider_call=_scripted_provider("A")). The prompt_template's placeholder substitution produces a prompt containing "decide" (because the gate node's template is "decide: {scene_input}"); the scripted provider sees that and returns "A", which populates state["gate_out"], which the router reads.
If your test is strictly about validation errors (e.g. "a branch referencing a missing conditional-edge target fails validate()"), you don't need to invoke — the validator catches it pre-compile. Keep the validation test as-is and add a sibling invoke test that exercises the valid-case path.
tests/test_branches.py — validation + serialization only. Gap:no compile+invoke of constructed branches.
tests/test_branch_definitions_db.py — storage round-trip. Gap:no compile+invoke after DB restore.
tests/test_storage_phase7_serializer.py — YAML round-tripidentity. Covered by test_conditional_edges_compile_invoke.py::TestYamlRoundTripStillRoutes.
tests/test_conditional_edges_surface.py — MCP build_branch /patch_branch surface. Gap: no post-build invoke.
tests/test_branch_visibility.py — auth/visibility with emptyconditional_edges fixtures. No routing semantics; skip.
tests/test_worldbuild_noop_integration.py — uses LangGraphdirectly, not BranchDefinition. Different seam; skip.
tests/test_graph_topology.py — tests hand-written routerfunctions directly (not the BranchDefinition path). Not the same surface; skip.
Add compile+invoke coverage when touching any file in the first group.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.