Uncheck icons for each new project

You use UI::Command instances.

But you do NOT use a hardcoded validation Proc.
Instead you access a module variable that holds a nested hash of the check states.
Outer hash has model objects as keys, inner hash has command symbols as keys.

module Author::SomePlugin

  extend self
  @@loaded ||= false

  @@command ||= {}
  @@checkstate ||= Hash::new(MF_UNCHECKED)

  def nifty_thing()
    # command code
  end

  def expectsStartupModelNotifications
    return true
  end

  def onActivateModel(model) # Mac ONLY
    unless @@checkstate.empty?
      @@checkstate.delete_if {|model_key,h| !model_key.valid? }
    end
  end

  def onNewModel(model)
    reset_checkstates(model)
  end

  def onOpenModel(model)
    reset_checkstates(model)
  end

  def reset_checkstates(model)
    if @@checkstate[model] == MF_UNCHECKED || !@@checkstate[model].is_a?(Hash)
      @@checkstate[model]= Hash::new(MF_UNCHECKED)
    end
    @@checkstate.delete_if { |model_key,h| !model_key.valid? }
    @@checkstate[model].each do |model_key,command_checkstates|
      command_checkstates.each do |command,check|
        check = MF_UNCHECKED
      end
    end
  end

  unless @@loaded

    # Declare a command called "NiftyThing":
    @@command[:NiftyThing]= UI::Command::new("NiftyThing") {
      nifty_thing() 
    }.set_validation_proc {
      @@checkstate[Sketchup::active_model][:NiftyThing] rescue MF_UNCHECKED
    }
    # set icons for @@command[:NiftyThing]

    # ... other command declarations ...

    # Attach this module itself as an AppObserver:
    Sketchup.add_observer(self)

    @@loaded = true

  end

end