{"record":{"id":"29cc9cca8fa42343","repo":"affaan-m/ECC","slug":"a-current-bound-approved-draft-is-required","errorCode":null,"errorMessage":"a current bound approved draft is required","messagePattern":"a current bound approved draft is required","errorType":"validation","errorClass":"ClaimError","httpStatus":null,"severity":"error","filePath":"skills/operator-approval-loop/references/approval_claims.py","lineNumber":55,"sourceCode":"    if any(db.execute(f'PRAGMA {name}').fetchone()[0] != 1\n           for name in ('foreign_keys', 'recursive_triggers')):\n        raise ClaimError('required SQLite guards are disabled')\n    try:\n        db.execute('BEGIN IMMEDIATE')\n        yield\n        db.commit()\n    except BaseException as error:\n        db.rollback()\n        if isinstance(error, sqlite3.Error):\n            raise ClaimError('claim transaction failed; no permission granted') from error\n        raise\n\n\ndef _snapshot(db, obligation_id, decision_id):\n    row = db.execute('''SELECT * FROM approval_bound_drafts\n        WHERE obligation_id=? AND decision_id=?''', (obligation_id, decision_id)).fetchone()\n    if row is None:\n        raise ClaimError('a current bound approved draft is required')\n    try:\n        digest = hashlib.sha256(row['draft_text'].encode('utf-8')).hexdigest()\n    except (AttributeError, UnicodeError) as error:\n        raise ClaimError('approved text must be valid UTF-8 text') from error\n    stored_digest = row['draft_sha256']\n    if (not isinstance(stored_digest, str) or len(stored_digest) != 64\n            or any(character not in '0123456789abcdef' for character in stored_digest)):\n        raise ClaimError('approved hash must be lowercase SHA-256 hexadecimal')\n    if not secrets.compare_digest(digest, stored_digest):\n        raise ClaimError('approved text hash does not match')\n    return dict(row)\n\n\ndef _claim_row(db, token):\n    if not isinstance(token, str) or not token:\n        raise ClaimError('a claim token is required')\n    row = db.execute('SELECT * FROM obligation_delivery_claims WHERE token=?', (token,)).fetchone()\n    if row is None:","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/operator-approval-loop/references/approval_claims.py#L37-L73","documentation":"Before granting any permission the library loads the immutable approval snapshot from approval_bound_drafts keyed by (obligation_id, decision_id). If no such row exists, there is no bound, approved draft to dispatch against, so it raises this error. It enforces that only previously approved content can ever be claimed or dispatched.","triggerScenarios":"Calling claim(db, obligation_id, decision_id, now=ts) with an obligation_id/decision_id pair never written by the trusted decision writer; calling begin_dispatch() whose claim references a snapshot row that was deleted; typos or wrong IDs (e.g. swapped argument order).","commonSituations":"Running the claims module against a database where the approval workflow never recorded the draft; pointing at the wrong environment's DB (staging vs production); a migration or cleanup job deleting approval_bound_drafts rows while claims still reference them.","solutions":["Verify the draft was recorded: SELECT * FROM approval_bound_drafts WHERE obligation_id=? AND decision_id=? before calling claim()","Correct the obligation_id/decision_id argument order or values","Ensure you are connected to the database where the approval actually happened","Restore the missing snapshot via the trusted decision writer; never hand-insert rows to bypass the approval flow"],"exampleFix":"// before\ntoken = claim(db, obligation_id, decision_id, now=ts)  # snapshot missing\n\n// after\nrow = db.execute('SELECT 1 FROM approval_bound_drafts WHERE obligation_id=? AND decision_id=?',\n                 (obligation_id, decision_id)).fetchone()\nif row is None:\n    raise AppError('no approved draft for this decision; run the approval flow first')\ntoken = claim(db, obligation_id, decision_id, now=ts)","handlingStrategy":"validation","validationCode":"def snapshot_exists(db, oid, did) -> bool:\n    return db.execute('SELECT 1 FROM approval_bound_drafts WHERE obligation_id=? AND decision_id=?',\n                      (oid, did)).fetchone() is not None","typeGuard":null,"tryCatchPattern":"try:\n    token = claim(db, oid, did, now=ts)\nexcept ClaimError as e:\n    if 'current bound approved draft is required' in str(e):\n        raise AppError(f'no approved draft for {oid}/{did}; run approval flow first') from e\n    raise","preventionTips":["Record the snapshot in approval_bound_drafts before ever calling claim()","Validate argument order (obligation_id, decision_id) at call sites","Point at the correct database/environment","Exclude approval_bound_drafts rows from cleanup/purge jobs, or purge claims with them"],"tags":["sqlite","missing-record","approval"],"backgroundTag":"record-not-found","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}