Skip to content
Goatfied

security

Audit logging for autonomous code changes

Audit logging captures authorization chains, decision paths, and constraint checks when AI agents modify code autonomously beyond standard Git metadata.

2026-09-088 min readBy Goatfied
Audit logging for autonomous code changes

When an AI agent modifies production code at 3 AM because it's acting on an automated workflow trigger, the question isn't just "did it work?" but "can we reconstruct exactly what happened, who authorized it, and why?" Traditional commit logs capture the what, but autonomous systems need audit trails that capture intent, authorization chains, constraint violations, and the decision path that led to each change.

The gap shows up fast: a developer reviews a pull request with 47 files changed, notices a database migration that wasn't in the original task description, and asks "why did the agent add this?" Without structured audit logs, you're left parsing LLM output for clues. With proper logging, you can trace the migration back to a constraint violation (the agent detected a schema mismatch), the tool call that triggered the fix, and the approval policy that allowed schema changes in this context.

What belongs in an AI code change audit log

Standard Git metadata—author, timestamp, commit message—doesn't capture enough. You need:

Authorization context: Which user or service account initiated the workflow? What approval gates passed? If the agent ran in response to a webhook, log the triggering event and its signature. For scheduled tasks, log the cron definition and the service principal.

Task decomposition: The agent's initial plan, including its understanding of the request and constraints it acknowledged. When an agent receives "fix the failing CI tests," logging should capture its interpretation: "identified 3 failing unit tests in auth module, plan to update mock data and fix timezone handling."

Tool call sequences: Every file read, write, shell command, or API request. Include both successful operations and denials. If your system rejected a tool call because it tried to modify a file outside the workspace boundary, that rejection is signal—it shows the agent attempted something risky and your constraints worked.

Constraint evaluations: When the agent's proposed change hit a compile error, linting violation, or test failure, log the specific error and the agent's response. The agent loop at the core of Goatfied—plan, constrain, edit, validate, retry—means you'll see multiple edit attempts. Auditors need to see not just the final working code, but the invalid attempts that were rejected.

Diff-level annotations: Tie each code change to the reasoning that produced it. If the agent added a null check in line 47, the audit log should reference the plan step or validation failure that motivated that specific edit. Small, reversible diffs make this traceability practical—you're logging intent for a 5-line change, not a 500-line refactor.

Approval decisions: If a human reviewed and approved a change mid-workflow, capture their identity, timestamp, and any comments. If an automated policy allowed the change (e.g., "documentation-only changes bypass review"), log which policy matched and its conditions.

Here's what a structured log entry might look like for a single edit operation:


{

  "event_id": "edit_20241205_034712_a4f3",

  "timestamp": "2024-12-05T03:47:12Z",

  "workflow_id": "fix-ci-failures-prod",

  "initiated_by": "user:alice@example.com",

  "agent_loop_iteration": 2,

  "operation": "file_write",

  "path": "src/auth/session.ts",

  "diff_hash": "sha256:9f3d...",

  "plan_step_id": "step_003",

  "plan_description": "fix timezone handling in session expiry",

  "constraints_evaluated": ["typescript_compile", "eslint", "unit_tests"],

  "constraint_results": {

    "typescript_compile": "pass",

    "eslint": "pass",

    "unit_tests": "fail:auth.test.ts expects UTC, got local time"

  },

  "retry_reason": "unit test failure"

}

The next iteration would show the agent's corrected attempt and a passing test result. An auditor can reconstruct the full edit sequence without re-running the agent.

Immutable log storage and retention

Audit logs are useless if they're editable. You need append-only storage with cryptographic integrity checks. In a self-hosted Goatfied deployment, this might mean writing logs to an S3 bucket with object lock enabled, or to a dedicated audit database where even admins can't modify records without leaving a trace.

For compliance-heavy environments, consider structured formats that tamper-evident logging systems can ingest: each log entry includes a hash of the previous entry, creating a verifiable chain. If someone tries to delete or alter an entry, the chain breaks and you can detect it.

Retention requirements vary—GDPR might let you purge old logs after a reasonable period, but financial services regulations often demand seven-year retention. Separate your log tiers: high-detail operational logs (tool calls, intermediate diffs) can expire after 90 days, while high-level audit events (who authorized what workflow, final commit hashes) live longer.

One practical pattern: emit logs to both a fast, searchable datastore for recent queries and a cold archive for compliance. Developers searching "why did the agent change this function last week?" hit PostgreSQL with indexed JSON columns. Auditors requesting "show me all schema changes in Q2 2023" query your cold archive, accepting slower results in exchange for complete history.

Integrating with existing security tooling

Your organization probably already has a SIEM, log aggregation pipeline, or compliance dashboard. Treat AI code change logs as first-class security events and route them through existing infrastructure.

Emit logs in a standard format like JSON or Common Event Format (CEF) so your SIEM can parse them without custom parsers. Tag events with severity levels: an agent modifying a lockfile might be INFO, but an agent attempting to write to /etc in a sandboxed environment is WARNING (the sandbox blocked it), and a workflow that bypassed required approvals is CRITICAL.

Set up alerts for anomalies: if an agent typically makes 3-5 tool calls per task but suddenly makes 50, that might indicate a runaway loop or an adversarial prompt. If an agent starts modifying files it's never touched before, flag for human review. Your SIEM probably already does anomaly detection for login patterns—apply the same logic to tool usage patterns.

For GitHub/GitLab users, surface audit metadata in pull request comments. When the agent opens a PR, include a summary: "This change was initiated by workflow X, made 2 edit attempts, passed all compile/lint/test gates, and matches approval policy Y." Link to the full audit trail in your logging system. Reviewers get context without leaving their code review tool.

Query patterns auditors actually need

Build your log schema around real questions:

Traceability: "Show me every code change that resulted from workflow ID X." Join workflow execution logs with file modification events. If a workflow spawned multiple agent loops (e.g., fixing one file triggered validation errors in another), your query should return the full dependency graph.

Attribution: "Which workflows touched this file in the past month?" Index on file paths and timestamps. Bonus points if you can show why each workflow touched the file—was it the primary target or a side effect of fixing imports?

Policy compliance: "Did any schema changes deploy without a migration review in the past quarter?" Filter for events where operation=schema_change and approval_policy != migration_review. If your answer is "I'd have to check Git history and guess," your audit logs aren't detailed enough.

Incident reconstruction: "This bug appeared in production on Tuesday. What changes did the agent make to this module on Monday?" Time-range queries filtered by file path, with diffs inline. Include not just successful changes but rejected attempts—maybe the agent tried a fix that didn't pass tests, then deployed a different fix that passed tests but introduced the bug.

Optimize for these queries by indexing on workflow ID, user ID, file path, timestamp, and event type. If you're storing logs in PostgreSQL, a GIN index on JSONB columns makes flexible queries fast. If you're using a time-series database like ClickHouse, partition by date and workflow ID.

The approval checkpoint pattern

Not every autonomous change should proceed without human oversight. Goatfied's constraint system lets you define approval gates: the agent can plan and generate code, but before finalizing a commit, it must pause for review if certain conditions match.

Log the checkpoint: when did the agent pause? What change was it proposing? Who approved or rejected it, and when? If the agent sat waiting for approval for three hours because the on-call engineer was in a meeting, that's operationally relevant—maybe you need broader approval authority for low-risk changes.

For truly autonomous workflows (e.g., an agent that fixes simple linting violations and directly commits to a feature branch), the audit log is your safety net. If something goes wrong, you can trace exactly what the agent did without human intervention. The log should be detailed enough that you could, in theory, revert the change and re-run the workflow with additional constraints to prevent the same mistake.

Related posts

Audit logging for autonomous code changes | Goatfied Blog