Scholarly publication: digest-bound approval invariants
These rules apply to CLI (vox db publication-submit-local, publication-external-jobs-tick), MCP (vox_scientia_publication_submit_local, vox_scientia_publication_external_jobs_tick), and the shared worker in vox_publisher::scholarly_external_jobs.
Dual approval
- Before any outbound scholarly submit or retry, the store must record two distinct approvers bound to the current manifest digest (
publication_manifests.content_sha3_256). - Enforcement:
VoxDb::has_dual_publication_approval_for_digest(and equivalent checks in operator paths). - If approval is missing, the operation fails fast (CLI error, MCP tool error, or tick
preflight_rejectedwith a retryable / permanent classification per message content).
Digest consistency
external_submission_jobs.content_sha3_256must match the live row inpublication_manifestsfor the samepublication_id. If the manifest changes, operators must create a new job or re-run submit so the job row aligns with the new digest.
Adapter routes
- New HTTP-backed adapters must {
- Respect
VOX_SCHOLARLY_DISABLE*(seescholarly::flags). - Return failures as
ScholarlyErrorsoerror_class,retryable, andscholarly_http_status_codepopulateexternal_submission_attemptsconsistently. - Use
classify_scholarly_httpfor HTTP error mapping unless the adapter needs venue-specific classification (then extend the shared helper rather than forking logic).
- Respect
Ledger pseudo-classes
- Job-only
last_error_classvaluepreflightis written when operator gates fail before adapter I/O. It is not aScholarlyErrorvariant.