I have a sub that wraps around a hash to keep track of some traits for some items, traits which may or may not have a specified value. Example:
One obvious way would be to use defined, as you say. It's a matter of encapsulation; I think it looks nicer if the code that uses this sub doesn't have to worry about that distinction. Another obvious way would be to use two different functions, but again, more visible complexity.
On the one hand, encapsulation is certainly often a Good Thing. On the other, the task you appear to be describing seems to be particularly well suited to Perl's hashes, which offer the distinction between exists and defined. Plus, you seem to want to mess with Perl's notion of Truth and Falsehood.
If I may wager a guess, perhaps you are taking concepts from a different programming language and trying to bend Perl to match them? While Perl is indeed a very pliable langauge and There Is More Than One Way To Do It, perhaps it would make sense to look at what Perl offers natively and implement what you're trying to implement in a perlish way first? For example, next to plain hashes, there are tied hashes, and objects can be overloaded to allow hash dereferencing of an object to return a reference to a hash, including a tied hash. That would allow users of your API to use the Perl hash concepts they are familiar with, while allowing you to keep control over what goes in and out of the hash.
Or, if you want to follow through with encapsulation idea, then it'd be best if you created classes and offered methods such as $bob->traits->get('age'), $bob->traits->is_set('isAdult'), and $bob->traits->set('foo','bar') (see e.g. the API of Hash::Ordered), because at the moment, it sounds to me like you're trying to overload your API a little too much.