How the Swarm-Forge Two-Call Audit Gate Invalidates and Resets on Changes

The Swarm-Forge two-call audit gate invalidates pending audits when any handoff details change by deleting mismatched fingerprint files, then requires a fresh audit with incremented counter on the next call.

The two-call audit gate in unclebob/swarm-forge ensures every Git handoff undergoes deliberate human review before queuing. This security mechanism relies on fingerprint comparison and explicit invalidation logic to prevent accidental or unauthorized task progression.

How the Two-Call Audit Gate Works

The First Call: Audit Request

When you invoke swarm_handoff.sh with a draft handoff, the script parses and validates the input but does not queue anything. Instead, it performs three critical actions (lines 60-73 in swarmforge/scripts/swarm_handoff.bb):

  • Prints AUDIT_REQUIRED to stdout
  • Increments the task's audit_count via increment-audit-count!
  • Writes a provisional audit record to .swarmforge/handoffs/audit_pending/<sender>/<sha256-task-id>.edn

The handoff stops here. The sender must manually re-read the complete task requirements and invoke the command again.

The Second Call: Unchanged Handoff Queued

If the second invocation produces an identical fingerprint to the pending audit, submit-after-audit! (lines 60-73) queues the handoff and deletes the audit files. The audit_count does not increment again—the audit gate has been satisfied.

Invalidation Trigger: Any Change Resets the Gate

The audit gate invalidates when any handoff component differs from the pending audit. Modifications that trigger reset include:

  • Different commit hash or base-commit
  • Changed recipient list
  • Altered task name or identifier
  • Modified draft content or priority
  • Updated non-forwarding flag

The Invalidation Mechanism

The invalidate-changed-invocation-audits! function (lines 311-318) handles cleanup with exclusive locking:

;; Pseudocode representation of the invalidation logic
(defn invalidate-changed-invocation-audits! [sender current-fingerprint]
  (with-exclusive-lock
    (doseq [audit-file (pending-audits-for sender)]
      (when (not= (:candidate (read-edn audit-file)) current-fingerprint)
        (delete! audit-file)))
    (remove-empty-directories! sender)))

After invalidation, the next swarm_handoff.sh call creates a fresh audit, increments audit_count again, and prints AUDIT_REQUIRED.

Fingerprint Generation and Comparison

The invocation-fingerprint function (lines 99-109) captures every handoff detail:

(defn invocation-fingerprint
  "Returns a hash-map of all handoff characteristics for audit comparison."
  [sender task-id type recipients priority task commit base-commit non-forwarding? draft-content]
  {:sender sender
   :task-id task-id
   :type type
   :recipients recipients
   :priority priority
   :task task
   :commit commit
   :base-commit base-commit
   :non-forwarding? non-forwarding?
   :draft-hash (sha256 draft-content)})

Two fingerprints must match exactly for the second call to proceed.

Practical Example: Complete Two-Call Flow


# First call – audit requested, counter increments

$ swarm_handoff.sh feature-handoff.edn
AUDIT_REQUIRED
HANDOFF_NOT_QUEUED
TASK_ID: HTW-2847
COMMIT: a1b2c3d4e5f6

# Inspect pending audit (optional verification)

$ ls .swarmforge/handoffs/audit_pending/alice/
a3f7...9e2d.edn

# Second call – unchanged draft, handoff queues

$ swarm_handoff.sh feature-handoff.edn
HANDOFF_QUEUED
TASK_ID: HTW-2847

Example: Change Invalidates Pending Audit


# Modify the draft before second call

$ sed -i 's/commit: a1b2c3d4e5f6/commit: deadbeef1234/' feature-handoff.edn

# New call – previous audit invalidated, fresh audit required

$ swarm_handoff.sh feature-handoff.edn
AUDIT_REQUIRED
HANDOFF_NOT_QUEUED
TASK_ID: HTW-2847
COMMIT: deadbeef1234

# audit_count incremented again

Source Code Locations

Component Location Lines
Audit gate documentation README.md 277-282
Fingerprint generation swarmforge/scripts/swarm_handoff.bb 99-109
Invalidation logic swarmforge/scripts/swarm_handoff.bb 311-318
Submit-after-audit orchestration swarmforge/scripts/swarm_handoff.bb 60-73
Test coverage test/swarmforge/handoff_test.clj (throughout)

Summary

  • First call creates pending audit, prints AUDIT_REQUIRED, increments audit_count
  • Second unchanged call matches fingerprint, queues handoff, clears audit files
  • Any change triggers invalidate-changed-invocation-audits! to remove stale audits
  • Changed call starts fresh audit cycle with new counter increment
  • Fingerprint covers all handoff metadata including draft content hash

Frequently Asked Questions

What exactly triggers audit invalidation in Swarm-Forge?

Any modification to the handoff draft invalidates the pending audit. This includes commit hash changes, recipient list updates, task identifier edits, priority adjustments, or even whitespace differences in the draft content that alter the SHA-256 hash computed in invocation-fingerprint (lines 99-109).

Where are pending audit files stored between calls?

Pending audits live at .swarmforge/handoffs/audit_pending/<sender>/<sha256-task-id>.edn. The directory structure organizes by sender to enable efficient cleanup in invalidate-changed-invocation-audits! (lines 311-318), which iterates only that sender's pending files.

Does the audit_count increment on every call or only audit requests?

The audit_count increments only when a new audit is created, not on every call. The first call to an unaudited handoff increments it. A matching second call does not. A changed call that triggers invalidation and fresh audit increments it again.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →