Skip to content
Incident Management

Building a Deployment Changelog That Survives an Incident

A 10-field deployment record schema that captures artifact identity, authorship (including AI agents), and approval chains for rapid incident triage.

Intermediate 7 min read Updated May 2026

When an incident begins, your team's first question is: "What changed?"

Most organisations have deployment logs. But those logs often capture that a deployment happened, not what actually deployed or who (or what) made the decision to deploy it.

When the code was authored by an autonomous AI agent, the gap becomes critical: you need to know that the agent made the deployment decision, what constraints it was operating under, and whether a human actually approved it.

This article defines a 10-field deployment record schema that survives an incident — cryptographically verifiable, queryable, and directly linked to artifact provenance and SBOM data.

The Problem with Existing Deployment Logs

Standard deployment logging captures:

deployment_id: "dep-abc123"
timestamp: "2026-04-29T14:23:45Z"
service: "payment-api"
status: "SUCCESS"

During an incident, this tells you nothing useful:

  • Which artifact was deployed? (Missing)
  • Which commit was in that artifact? (Missing)
  • Who approved the deployment? (Missing)
  • Was the code reviewed? (Missing)
  • Did an agent author it? (Missing)
  • What dependencies does the artifact contain? (Missing)

You're forced to manually trace through git logs, CI/CD dashboards, and deployment systems to reconstruct the picture. By then, the incident is 3 hours old.

The 10-Field Deployment Record Schema

{
  "deployment_id": "dep-abc123def456",
  "timestamp": "2026-04-29T14:23:45Z",
  "service_name": "payment-api",
  
  // Field 1: Artifact Identity
  "artifact_digest": "sha256:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0",
  
  // Field 2: Source Commit
  "source_commit_sha": "abc123def456ghi789jkl012mno345pqr678stu",
  
  // Field 3: SBOM Reference (for CVE triage)
  "sbom_digest": "sha256:x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6n7o8p9q0",
  
  // Field 4: Authorship (CRITICAL for AI governance)
  "authored_by": "autonomous-agent|human|co-authored",
  
  // Field 5: Agent Metadata (if applicable)
  "agent_name": "replit-agent",  // or null if human-authored
  "agent_version": "v1.2.3",
  "agent_model": "claude-3-opus",
  "agent_constraints_enforced": {
    "code_freeze": true,
    "destructive_operations": false,
    "requires_human_approval": true
  },
  
  // Field 6: Human Approval
  "approved_by": "[email protected]",
  "approval_timestamp": "2026-04-29T14:20:00Z",
  "approval_required": true,
  "approval_provided": true,
  
  // Field 7: Deployment Target
  "target_environment": "production|staging|dev",
  "target_region": "us-east-1",
  "replicas_deployed": 5,
  
  // Field 8: Build Context
  "build_system": "GitHub Actions",
  "build_id": "run-abc123",
  "build_timestamp": "2026-04-29T14:15:32Z",
  
  // Field 9: Signature for Tamper-Evidence
  "deployment_record_signature": "-----BEGIN SIGNATURE-----...",
  "signature_algorithm": "RSA-2048",
  
  // Field 10: Change Summary
  "change_summary": {
    "files_changed": 3,
    "lines_added": 47,
    "lines_deleted": 12,
    "test_coverage_before": 87.3,
    "test_coverage_after": 88.1
  }
}

Why Each Field Matters During Incidents

Fields 1–3: Artifact Traceability

artifact_digest, source_commit_sha, sbom_digest

When an incident fires, knowing the exact artifact running and its commit enables:

  • Fast SLSA provenance lookup (did this build come from a malicious commit?)
  • SBOM-based CVE triage (does this artifact contain known-exploited vulnerabilities?)
  • Rollback planning (what exact version are we rolling back from?)

During incident: "What's the digest of the running pod?" → Match to deployment record → "This is commit abc123" → "Check that commit's diff"

Fields 4–5: AI Authorship Metadata

authored_by, agent_name, agent_version, agent_constraints_enforced

This is the critical gap in current deployment logs. If an autonomous agent authored the code AND made the deployment decision, your team needs to know immediately.

During incident with agent-authored code:

  • Was the agent operating under the right constraints? (constraint check)
  • Did the agent violate code freeze, approval requirements, or scope limits? (escalation signal)
  • Which agent version? (reproduction path)
  • Which model? (behavior context)

During incident with human-authored code:

  • All agent fields are null/empty
  • Your team proceeds with standard incident response

Field 6: Approval Chain

approved_by, approval_timestamp, approval_required, approval_provided

This field answers: "Did a human actually review and approve this deployment?"

For AI-authored code, this is mandatory. If approved_by is empty, approval_provided is false, the agent made an unsupervised deployment decision — immediate escalation.

Fields 7–8: Context

target_environment, region, replicas_deployed, build_system, build_id, build_timestamp

These enable blast-radius assessment and reproduction:

  • If the incident is in production but the deployment targeted staging, something is wrong
  • If the build happened 6 hours before deployment, there's a timing anomaly
  • If replicas_deployed is 1 but the service usually runs 5, rollout is incomplete

Field 9: Tamper-Evidence

deployment_record_signature, signature_algorithm

The deployment record itself must be cryptographically signed so that:

  • If the record is modified after the fact, the signature fails
  • During forensics, you can prove the record wasn't backdated or altered
  • Compliance auditors can verify chain of custody

Field 10: Change Summary

change_summary

Fast context: "This commit changed 3 files, added 47 lines" enables quick judgment of risk level. Massive diff = higher risk. One-liner fix = lower risk.

Implementation Patterns

Pattern 1: CI/CD Native (GitHub Actions / GitLab CI)

# .github/workflows/deploy.yml
name: Deploy

on: workflow_dispatch

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build artifact
        run: docker build -t myapp:${{ github.sha }} .
      - name: Generate SBOM
        run: syft myapp:${{ github.sha }} -o json > sbom.json
      - name: Create deployment record
        run: |
          cat > deployment_record.json << 'EOF'
          {
            "deployment_id": "${{ github.run_id }}-${{ github.run_number }}",
            "timestamp": "$(date -Iseconds)",
            "service_name": "payment-api",
            "artifact_digest": "sha256:$(docker inspect --format='{{.Id}}' myapp:${{ github.sha }}))",
            "source_commit_sha": "${{ github.sha }}",
            "sbom_digest": "sha256:$(sha256sum sbom.json | cut -d' ' -f1)",
            "authored_by": "${{ contains(github.event.head_commit.message, 'AI-AUTHORED') && 'autonomous-agent' || 'human' }}",
            "approved_by": "${{ env.APPROVER }}",
            "approval_timestamp": "$(date -Iseconds)",
            "target_environment": "production",
            "target_region": "us-east-1",
            "replicas_deployed": 5
          }
          EOF
      - name: Sign deployment record
        run: |
          openssl dgst -sha256 -sign ${{ secrets.DEPLOY_KEY }} \
            -out deployment_record.sig deployment_record.json
      - name: Push deployment record to log
        run: |
          curl -X POST https://deployment-log.internal/records \
            -H "Authorization: Bearer ${{ secrets.DEPLOY_LOG_TOKEN }}" \
            -d @deployment_record.json \
            -H "X-Signature: $(cat deployment_record.sig | base64)"
      - name: Deploy
        run: kubectl apply -f deployment.yaml

Pattern 2: Query During Incident

# Find all deployments to payment-api in the last 2 hours
curl -s https://deployment-log.internal/query \
  -d '{
    "service_name": "payment-api",
    "time_range": "last_2h",
    "include_signatures": true
  }' | jq '.'

# Output: [deployment_record_1, deployment_record_2, ...]

# Verify signatures
for record in $(jq -r '.deployment_record_signature' deployment_records.json); do
  openssl dgst -sha256 -verify $PUBLIC_KEY -signature <(echo $record | base64 -d) deployment_record.json
done

# Check: did an agent deploy?
jq '.[] | select(.authored_by == "autonomous-agent")'

# Result: If this returns records, agent was involved — escalate

Retention and Compliance

Deployment records should be:

  1. Immutable — Signed and stored in append-only log (Rekor, or similar)
  2. Queryable — Indexed by service, timestamp, author, agent name
  3. Retained — for high-risk AI systems under the EU AI Act, Article 18 requires 10-year retention from the date the system is placed on the market (applies from 2 August 2026, alongside the rest of the high-risk regime; GPAI-only providers fall under the 2 August 2025 obligations instead, which are different); NIST and FedRAMP recommend 5–7 years minimum
  4. Auditable — Every access to records logged; changes require authorization

Using Deployment Records for Incident Root Cause

Classic incident progression:

T+0h00m: Incident alert fires (error rate spike)
T+0h03m: Deploy record query shows last deployment was 47 minutes ago
T+0h05m: Agent-authored code detected in that deployment
T+0h07m: SBOM query shows deployment contains a KEV entry (known-exploited vuln)
T+0h10m: Rollback to previous deployment (no agent, no KEV)
T+0h15m: Incident resolved; agent constraints review scheduled

Without deployment records, this becomes:

T+0h00m: Incident alert fires
T+0h30m: Team discovers a deployment happened recently (from git log)
T+3h00m: Manual review of diff, build logs, deployment manifests
T+6h00m: Root cause narrowed (maybe)
T+8h00m: Rollback decision made

The 10-field record compresses diagnosis from hours to minutes.


References

This article is part of the Incident Management knowledge series (7 articles) Browse all Incident Management articles →
Related Use Case

Incident Response — A CVE drops Friday at 4:47.

Ask the artifacts.

Explore Use Case →