code: func (r *ruleImpl) CallInternal(th *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
  	newArgs := make(starlark.Tuple, 0, len(args)+1)
  	newArgs = append(newArgs, starlarkstruct.FromStringDict(
  		starlark.String("lucicfg.rule_ctx"),
  		starlark.StringDict{
  			"defaults": r.defaults,
  		},
  	))
  	newArgs = append(newArgs, args...)
  	switch res, err := r.Callable.CallInternal(th, newArgs, kwargs); {
  	case err != nil:
  		return nil, err
  	case !isNamedStruct(res, "graph.keyset"):
  		return nil, fmt.Errorf("bad rule implementation %s: must return graph.keyset, got %s", r.Callable, res.Type())
  	default:
  		return res, nil
  	}
  }
docs: // CallInternal prepends `ctx` argument and validates the return value.
  //
  // Note that we intentionally do not start a new stack frame to declutter stack
  // traces: lucicfg.rule(...) wrapping is "transparent" from Starlark's point of
  // view.