- embed_note / embed_insight / embed_source now share a single
load->embed->write runner (_embed_record) with one common
ValueError/Exception epilogue; note and insight additionally share
_embed_markdown_record for the identical load/validate/embed/UPSERT body
- rebuild_embeddings submits jobs for sources/notes/insights through one
_submit_embedding_jobs helper instead of three copy-pasted loops
- the four embed-family commands reuse one EMBED_RETRY_CONFIG dict, with a
NOTE marking that stop_on can never trigger today (commands catch
ValueError internally) - preserved as-is for a future error-handling PR
- full_model_dump() was copy-pasted in three command files but only used by
podcast_commands: moved to open_notebook/utils/model_utils.py, imported
where used, dead copies in embedding/source commands removed
Behavior-identical: same outputs, success=False paths, log messages and
retry configuration. 403 tests pass; ruff and mypy clean (no new errors).