Don, you were exactly right.
Just for everyone else that might find this thread, here's my corrected ad_form declaration:
----------------
ad_form -name add_edit -form {
    brand_id:key
    ... declaration of form elements
} -select_query_name brand_select -on_submit {
    set user_id [ad_conn user_id]
    set peeraddr [ad_conn peeraddr]
} -new_data {
    db_transaction {
      db_exec_plsql new_brand { }
      db_dml brand_update { }
    }
    ad_returnredirect "."
    ad_script_abort
} -edit_data {
    db_dml brand_update { }
    ad_returnredirect "."
    ad_script_abort
}
-----------
The bug was in the add-edit-postgres.xql file. Here is how it was:
<fullquery name="new_brand">
       <querytext>
       select brand__new(
          null,
          :name,
          :name,
          :notes,
          'f',
          [db_nextval brand_brand_lid_seq],
          'brand',
          now(),
          :user_id,
          :peeraddr,
          :package_id
       );
      </querytext>
</fullquery>
-------------
If you look at the declaration of brand__new, the first parameter is brand_id. I was passing it null, so it wasn't getting the brand_id that was generated by ad_form.
Hopefully, this will help out someone else that makes the same mistake I did.
For the search engine: postgres 16 argument ad_form key changes