Applied AI in AutomationLab

How I run the lab with AI and automation

I run AutomationLab by myself. Ansible handles operating-system configuration, Kubernetes runs the container workloads, and advanced automation carries the recurring work through Jenkins and the other delivery paths. Keeping a lab this size moving also requires AI. I use it to build and review changes, work through unfamiliar failures, and make the overnight evidence manageable enough to act on.

That still leaves me responsible for what runs. I choose the work and the operating boundaries, then use an experimental fleet of Claude and Codex agents to work through scoped repository changes. We use cross-vendor reviews in both directions: Codex reviews Claude's changes, and Claude reviews Codex's. Tests and a separate merge process check the result before it can reach the main branch.

The fleet is experimental and often stalls. I run it within a limited AI usage budget and shut it off when higher-priority projects need that budget. The map below explains the architecture; the Live section links to the public activity map.

The work has to keep moving when I am busy elsewhere

A certificate renewal, an interrupted server build, and a failed backup can all need attention on the same day. I can automate the known steps, but I still need to understand new failures and keep improving the code that handles them. AI gives me help with that reading and implementation work while the existing pipelines keep doing their jobs.

I use the models where judgment is useful and the output can be inspected. A repository change becomes a pull request with a diff and test results. Morning triage produces findings linked to collected evidence. A scanner explanation stays beside the original finding so I can see what the model was asked to interpret.

My part
Choose priorities, define the scope, investigate unresolved failures, and decide which operational actions are allowed.
Automation's part
Collect evidence, launch bounded jobs, run tests, enforce identities, and record where a workflow stopped.
AI's part
Read the supplied context, implement a scoped change, review another provider's work, or explain a finding.
Current limit
The fleet is experimental. A green build and a second model's review still leave room for a shared misunderstanding, an incomplete test, or a defect that appears only when the change runs.

The server provisioning story shows what Ansible and the delivery pipelines own once a request is approved. This page follows how I use AI to develop and support those workflows.

A public map of the experimental fleet

The private fleet map groups the controller, builders, and assigned reviewers so I can follow a piece of work through the system. This version keeps that hierarchy and shows both review directions. Each box represents a role. The number of running agents changes with available work and operating limits.

Experimental fleet: operator scope enters a Python controller, Claude builders go to Codex reviewers, Codex builders go to Claude reviewers, and passing branches reach separate delivery gates

On a narrow screen, scroll the map sideways or open the fleet map at full size. The map shows the workflow; it does not display live status.

The controller is Python code

I set the priorities and operating limits. The controller checks the queue and recorded state, assigns eligible work, and starts the appropriate command-line agent. A builder runs in an isolated Git worktree, which gives it its own branch and files. The launch and publication code records the result and opens the pull request.

That separation is useful when an agent stops halfway through. The controller can distinguish a running job from a completed process with no deliverable, arrange a bounded retry or repair, and park work that has exhausted its attempts. I can then inspect the recorded failure instead of starting from an agent's claim that it finished.

Claude builds, Codex reviews

The Codex reviewer starts with fresh context and checks the proposed change against its task, diff, and evidence. It records findings against the commit it actually read.

Codex builds, Claude reviews

The same handoff runs in the other direction. The reviewer has a separate identity and a read-only job, so correcting the branch remains the builder's responsibility.

Delivery has its own checks

A passing review is one input to delivery. The separate merge executor also requires successful CI on the current commit and a mergeable branch, subject to the fleet's operating controls. If a repair changes the commit, the old verdict cannot authorize that revision. Repository delivery and an infrastructure apply remain separate operations.

I am still testing whether this arrangement reduces the time I spend getting useful work delivered. More agents also mean more opportunities for conflicting work, stalled jobs, and reviews that repeat the same mistake. I keep the scope and retry budgets bounded, and I need a way to stop the fleet and inspect its state.

Cross-vendor review gives the change a second reading

I ask the other provider to review because the author has already settled on an explanation of the problem. A fresh reviewer can question that explanation, inspect the failure path, and compare the implementation with the original scope. Changing vendors is useful for that second reading, although it cannot guarantee that the models will make different mistakes.

The task gives the reviewer something to test

A task names the objective, allowed files, expected evidence, and stop conditions. For an Ansible change, that might mean checking the intended operating-system behavior and the rerun after a partial failure. The reviewer needs to know what success means before it can judge whether the branch delivers it.

  1. Build on an isolated branch. The agent reads the task and repository instructions, makes the scoped change, and runs the relevant checks. Publication preserves the diff and evidence in a pull request.
  2. Review with the other provider. The reviewer reads the task and the current diff with fresh context. Its verdict names the commit under review and any defects it found.
  3. Repair a named defect. A failed review returns findings to the builder through a bounded repair path. The corrected commit needs new CI and a fresh review.
  4. Check delivery state again. The merge process evaluates the current commit, its checks, its review, and whether the branch can merge. Exhausted repairs or unresolved failures need my attention.

A parser fix needed that second pass

A scanner allowlist parser was replaced after a TOML handling defect. Cross-vendor review found that the first correction still mishandled malformed configuration. The correction needed checks for absent, unreadable, garbled, and wrong-typed input. A branch that handled the expected file was still incomplete.

Read the scanner failure

That is the kind of review I want: a specific input, the behavior that breaks, and evidence that the correction covers it. Agreement between two models is not enough by itself.

CLI sessions and API requests have different controls

I use subscription-backed Claude and Codex command-line sessions for repository work and selected unattended pipeline analysis. Services that need an API use LiteLLM, the shared model gateway. These paths have different credentials, usage measurements, and failure behavior.

CLI jobs pass through a subscription selector to Claude or Codex; API consumers pass through LiteLLM to local or approved external models; both validate results before a separate action

Subscription-backed CLI work

The fleet launches isolated repository agents. Selected pipelines also use a shared headless router for bounded analysis. Subscription capacity belongs to those CLI sessions; it does not provide an API credential to another service.

Gateway API work

LiteLLM authenticates a consumer, applies its configured budget, and routes the request to a local model or an approved external provider. The calling application still has to validate the response.

The subscription routing project follows the pipeline selector. The LiteLLM page explains the API budgets and their limits.

Morning triage gives me somewhere to start

I do not want to start the morning by reading every overnight log separately. The AIOps path starts with collectors. Monitoring, CI, backup, and delivery systems produce structured records with source timestamps and status. Code normalizes those records, applies known-noise dispositions, checks freshness, and groups exact relationships that do not require a model.

The model receives the remaining ambiguous bundle. It can summarize likely relationships, identify missing context, and draft an actionable finding. The digest keeps separate categories for actionable work, needs information, and known noise. The source records remain available for each claim.

  1. Collectors read each source. They write timestamped records and completion state. A failed collector does not produce an empty healthy input.
  2. Code normalizes and correlates known cases. Stable identifiers, time windows, duplicate suppression, and approved dispositions run before inference.
  3. The analysis runner receives a bounded bundle. The subscription-backed analysis path receives the collected evidence. Source text is treated as data, and the result still has to pass validation.
  4. The model returns a constrained structure. Findings name their evidence, disposition, severity, and missing information.
  5. The pipeline validates and stores the result. Malformed output fails. Valid findings enter the digest and tracking path.
  6. The delivery monitor expects the report. The dead-man switch alerts when the daily digest or its checker goes silent.

The morning triage project follows collection through the digest in more detail. The incident analyst uses the same shape at a smaller scale. It summarizes the evidence attached to one failure so the first response starts with context. It does not change the alert or perform the repair.

Scanner triage cannot turn a broken scan green

Secret, container, and infrastructure scanners find candidate problems. A local model can classify a result against the deployed context and reduce noise where the policy allows. The model is downstream of the scanner.

If the scanner did not run, the input is incomplete, or the model response is missing or malformed, the original finding keeps its severity. The model cannot invent a clean scan. Secret values are removed before the request, and the model receives the finding text inside a delimiter that marks it as untrusted data.

Model outputPipeline decisionRecorded evidence
Valid actionable dispositionKeep or escalate the finding.Source scanner result plus model explanation.
Valid likely-noise dispositionDowngrade only within the approved policy.Source result, contextual reason, model and policy version.
Malformed responseLeave the source finding unchanged.Validation failure and original result.
Provider unavailableLeave the source finding unchanged.Provider failure and retry state.
Scanner missingFail the required check.Absence of scanner completion, not an empty list.

Routing considers the task, its data, and available capacity

I use a local open-weight model for frequent classification that can stay inside the lab. Approved external models handle repository-scale reasoning and harder analysis when the consumer's route allows it. The data supplied to a request still has to fit that route.

LiteLLM gives API consumers separate virtual keys, per-consumer budgets, and a shared spend ceiling. It records spend and failures, and its virtual keys follow the credential-rotation system. Those dollar budgets are separate from the subscription windows used by the command-line agents.

The fleet and pipeline selector handle missing usage differently

The fleet reads remaining subscription capacity and reset times to rest a depleted provider, balance work, and adjust concurrency within configured bounds. Fresh evidence of low capacity can stop new work for that provider. If the feed is missing or stale, the fleet records quota as unknown and continues using its configured fallback behavior. This quota check is an operating optimization, not a safety boundary.

The shared subscription selector used by unattended pipelines has a stricter reserve and freshness decision. If neither provider is eligible, that pipeline path stops. I keep these behaviors separate because saying that all AI work stops on stale usage would misdescribe the fleet.

The fleet remains experimental partly because operating policy needs this much attention. Losing a usage feed can leave work consuming a subscription until another limit intervenes. Concurrency limits, per-run budgets, CI, review, and the kill switch remain separate controls.

Live subscription-capacity dashboard

The public board shows remaining capacity and the time-based ideal line for both subscription providers. Account identity, runtime tokens, worker labels, exporter internals, and private alert annotations are removed before the public data source.

Live public Grafana view

Open full screen

How to read it

Can a worker start now?

Compare actual with reserve

Fresh capacity above the configured floor may permit work. The fleet also checks its operating limits; unknown quota uses its fallback behavior.

Is usage pacing too fast?

Compare with the ideal line

Actual remaining capacity below the time-based line shows consumption ahead of the reset pace.

Why did a provider disappear?

Check collection freshness

A missing or old sample is not evidence of available capacity. The fleet and the pipeline selector respond differently to that uncertainty, as described above.

Does this show API spend?

No, this board is subscription capacity

LiteLLM spend and subscription usage are different resources with different collectors and gates.

A recommendation does not authorize its own action

Model mayModel may notEnforcement point
Edit a task-scoped branchMerge its own workWorktree isolation, cross-vendor review, and merge executor.
Summarize incident evidenceReplace the original alertSource records remain attached to the finding.
Recommend a remediationApply an unapproved infrastructure changeOperator approval and target-specific machine identity.
Classify a scanner findingCreate a passing result when scanning failedRequired scanner check and fail-closed disposition parser.
Draft an explanation from supplied evidenceRead secret values not supplied to its taskVault policy, request scrub, and consumer key scope.

Prompts are not treated as security boundaries. The boundary is the data supplied, the identity used, the network route, the output schema, and the action the next process is permitted to take.

What broke

The morning triage went silent for 19 days

The absence was eventually detected from missing run evidence, not from a digest that reported its own failure. The collector and freshness path was repaired and the dead-man control was expanded. The Jenkins-side cause of the original silence was never established, so this page does not assign one.

A model-generated scanner fix still needed another reviewer

The parser correction described in the review walkthrough handled the expected input before it handled malformed configuration safely. That is why I keep failure cases in the review scope.

Usage data can outlive the collector that produced it

The last successful reading can survive a failed collection and look current if its age is hidden. The collector exports freshness so consumers can recognize this case. The pipeline selector refuses stale capacity; the fleet records the uncertainty and continues under its fallback policy.

How it runs now

WhenWhat runsWhat happens next
On a repository taskingA subscription-backed CLI agent works in one isolated branch.Tests and a pull request carry the result to independent review.
HourlyThe subscription collector refreshes remaining capacity and reset time.The fleet adjusts provider selection and width; pipeline eligibility follows its separate reserve policy.
On an API inference requestAn authenticated consumer sends a bounded request through LiteLLM.Routing, budget, model response validation, and audit state are applied.
During morning triageCollectors, deterministic correlation, model analysis, storage, and email run in sequence.The digest separates actionable, needs-information, and known-noise findings.
On scanner outputThe model may propose a context disposition.The source scanner result remains authoritative on failure.
After a recommendationA pipeline, reviewer, or operator evaluates the proposed action.Any permitted action still uses the identity and approval required by that workflow.

If I were recreating this from scratch

  1. Choose one bounded decision. Start with classification, summarization, or code review where the input and output can be checked.
  2. Build deterministic collection first. The model should not be responsible for discovering whether its evidence exists.
  3. Define the identity and data boundary. Decide which Vault role, provider key, network path, and fields the request requires.
  4. Validate the output before using it. Use a schema and keep the source evidence beside the model result.
  5. Put action in a separate process. A recommendation, pull request, or disposition should cross a policy or operator gate before mutation.
  6. Measure provider capacity and freshness. Define the fallback explicitly. The pipeline selector stops on missing eligibility; the fleet currently continues on unknown quota.
  7. Add a dead-man signal. A silent collector or digest should become a named monitoring failure.