Beyond the IDE: Running Autonomous Agentic SDLC in Isolated Containers
In Part 1 and Part 2, I shared the governance rules that keep AI coding agents on track: Dual-PR Spec-First contracts and the adaptive L0โL3 auto mode.
However, one practical problem remained: everything still runs on the developer’s laptop.
If an agent needs 10 minutes to run tests, explore AST files, and do a review pass, the developer has to keep their laptop awake, avoid switching branches, and baby-sit terminal prompts. If you try to run agents in standard CI, they either crash when asking for interactive input or make wild guesses.
I wanted to take this entire workflow off developer machines and move it into an ephemeral, hardened container running in the background (asdlc run --container).
With the container design now finalized in agentic-sdlc, this post explains how the architecture works, how it handles human PR reviews without a terminal, and the trade-offs I made.
๐ฏ BLUF (Bottom Line Up Front)
- The Goal: Run the Agentic SDLC lifecycle unattended in a non-root Linux container (
ubuntu:26.04,UID 10001:10001), interacting with engineers strictly through Git and GitHub PR comments. - The “3+1” Stage Model: Instead of a “black box” that guesses what to do across days, the pipeline uses four clear stages:
--stage spec(drafting and linting),--stage apply(TDD loop),--stage verify(14 quality gates and review), and--stage fix(autonomous review comment fixes). - Async PR Comment Grilling: If requirements are unclear, the container opens a Draft Spec PR, posts questions in a PR comment, and shuts down immediately (zero idle compute cost). When a human replies on GitHub, re-running the command ingests the answers and moves forward.
- 3-Tier PR Review Triage: When colleagues leave review comments,
--stage fixautomatically classifies them into: code fixes (applies edits and reruns tests), technical questions (answers fromdesign.mdwithout editing code), and scope creep (politely defers to another issue). - Dual-Track Symmetry: The container mode does not break or replace the local IDE workflow. Both share the exact same Git repository and OpenSpec files as the single source of truth.
1. Why Running Agents on Laptops Doesn’t Scale
Watching tools like VS Code + Claude Code or terminal CLI agents write code in real time looks great in demos. But in real team workflows, running autonomous agents locally hits four clear walls:
- Terminal Babysitting: A full TDD loop and multi-round review often take 5 to 15 minutes. Developers have to keep their laptops open and wait.
- Workspace Contamination: Agents create build caches, temporary test files, and sometimes leave dirty Git state or mess up line endings (CRLF vs. LF on Windows).
- Complex Input: Real requirements don’t come as a short CLI string like
asdlc new "Fix payment retry". They come as Jira tickets, Figma links, and multi-page PRDs. - No TTY in Headless Mode: When an agent hits an unclear requirement, it prompts the user on standard input (
stdin). In headless cloud runners with no TTY, this either crashes or forces the agent to hallucinate an answer.
To make agentic coding practical at scale, the agent loop must move into an isolated container runtime.
2. Architecture: Host vs. Ephemeral Container Split
The core design separates host configuration from untrusted container execution:
flowchart TD
subgraph Host["Host Machine (Developer Laptop or CI Runner)"]
CLI["<b>asdlc run --container</b><br/>Host Orchestrator CLI"]
GCFG["~/.asdlc/config.yaml<br/>(Global Settings, Mode 0600)"]
ACFG["Native AI Configs<br/>(Claude Code / Gemini CLI / Anthropic)"]
CSKILL["~/.asdlc/skills/*<br/>(Jira / Confluence Skills)"]
HFILES["Host Design Docs (--from-file)<br/>(PRDs, SQL schemas)"]
GHTOKEN["Host Auth Extractor<br/>(gh auth token / Git Credentials)"]
end
subgraph Container["Ephemeral Container (ubuntu:26.04 LTS, UID 10001:10001, --rm)"]
WS["/workspace<br/>(Clean Linux Git Clone)"]
INTAKE["openspec/changes/<id>/intake/<br/>(Snapshotted PRDs and Issues)"]
subgraph SESSIONS ["Dual Process Isolation (Player != Referee)"]
S1["Session A: Author / Implementer<br/>(claude -p / gemini)"]
S2["Session B: Fresh Reviewer<br/>(Independent Spec & Impl Audit)"]
end
GATES["Deterministic Quality Gates<br/>(asdlc lint ยท test ยท verify)"]
OS_LOCK["Kernel Read-Only Spec Lock<br/>(proposal.md & specs/** locked 0444)"]
end
subgraph Remote["Git Platform (GitHub / GitLab)"]
PR1["Draft / Ready Spec PR<br/>(Async Q&A via PR Comments)"]
MAIN["Protected main Branch<br/>(Merge permanently locks specSha)"]
PR2["Implementation PR<br/>(Verified + AI Reviewed)"]
end
CLI --> GCFG
CLI --> ACFG
CLI --> CSKILL
CLI --> HFILES
CLI --> GHTOKEN
CLI -- "docker create + docker cp (chown 10001:10001)" --> Container
WS --> INTAKE
INTAKE --> S1
S1 --> S2
S2 --> GATES
GATES -- "Phase 1: Spec PR" --> PR1
PR1 -- "Human Tech Lead Merge" --> MAIN
MAIN -- "Phase 2: Unlock Implementation" --> OS_LOCK
OS_LOCK --> S1
GATES -- "Phase 2: Impl PR" --> PR2
3. The “3+1” Canonical Stage Execution Model
A common mistake in autonomous coding tools is trying to build a “magic black box” that runs from start to finish without human touchpoints.
In real teams, software delivery is naturally staged and asynchronous. You write a spec, wait for review, get approval, write code, get another review, and merge.
Instead of guessing state, agentic-sdlc uses Bounded Stage-Driven Execution:
THE "3+1" CANONICAL STAGES
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. --stage spec โ โ 2. --stage apply โ โ 3. --stage verify โ
โโโโโโโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโโโโโโค
โ Intake, AST explore, โ โ Task authoring and โ โ 14 quality gates and โ
โ spec drafting, AI โ โ task-level TDD Red- โ โ independent review; โ
โ review & Ready Spec PRโ โ Green loop (0444 lock)โ โ opens Impl PR โ
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโ
โ +1. --stage fix โ
โโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Autonomous triage and โ
โ resolution of human PR โ
โ review comment threads โ
โโโโโโโโโโโโโโโโโโโโโโโโโโ
- Bounded Execution: Inside each run, the container does its job automatically (TDD loop, gate checks, review comment handling).
- Clear Milestones: Between stages, the container stops and waits for human action on GitHub.
- Zero Idle Compute: When a stage finishes or hits a question, the container destroys itself (
--rm). It never sits idle waiting for someone to type.
The Full Lifecycle Sequence
sequenceDiagram
autonumber
actor Dev as Developer / Tech Lead
actor Rev as Peer Reviewer
participant Host as Host CLI (asdlc run)
participant Runner as Container (UID 10001)
participant AgentA as Session A (Author / Impl)
participant AgentR as Session B (Fresh Reviewer)
participant GitPR as GitHub / GitLab PR
Note over Dev,GitPR: Stage 1: Spec Authoring & Review (--stage spec)
Dev->>Host: asdlc run --from-issue JIRA-345 --from-file ./prd.md --stage spec
Host->>Runner: Start container + copy workspace, config, skills
Runner->>AgentA: Read intake/ files, explore AST, draft spec
alt Ambiguities Detected (OPEN items in clarifications.md)
AgentA->>GitPR: Open Draft PR (asdlc:needs-clarification) + post Q1..Qn
Runner-->>Host: Exit cleanly 0 (zero idle container)
Dev->>GitPR: Reply directly to Q1..Qn in PR comments
Dev->>Host: asdlc run #34 --stage spec
Host->>Runner: Ingest PR comments -> Clear OPEN items in clarifications.md
end
Runner->>AgentR: Spawn Session B (asdlc-spec-review, clean context)
Runner->>Runner: Run deterministic gate: asdlc lint (must pass)
Runner->>GitPR: Mark Ready Spec PR #34 + post review summary
Runner-->>Host: Destroy container (--rm)
opt Reviewer Leaves Feedback on Spec PR #34 (--stage fix)
Rev->>GitPR: Post review comments or suggestions on Spec PR #34
Dev->>Host: asdlc run #34 --stage fix
Host->>Runner: Pull unresolved review threads via GraphQL
Runner->>AgentA: Update proposal.md & specs/** to address feedback
Runner->>Runner: Run asdlc lint (must pass)
Runner->>GitPR: Push fix commit + reply to thread + resolve thread
Runner-->>Host: Destroy container (--rm)
end
Dev->>GitPR: Human Tech Lead reviews & merges Spec PR #34 into main (locks specSha)
Note over Dev,GitPR: Stage 2: TDD Implementation (--stage apply)
Dev->>Host: asdlc run feat-auth --stage apply
Host->>Runner: Clone branch, lock specs as 0:0 0444 read-only
Runner->>AgentA: Author tasks.md + run TDD loop (Red -> Green -> Refactor)
Runner->>Runner: Local test suite passes on all tasks
Runner-->>Host: Destroy container (--rm)
Note over Dev,GitPR: Stage 3: Verification & Impl Review (--stage verify)
Dev->>Host: asdlc run feat-auth --stage verify
Host->>Runner: Spawn Session B (asdlc-impl-review) + run asdlc verify (14 gates)
Runner->>GitPR: Open Implementation PR #42 + attach evidence
Runner-->>Host: Destroy container (--rm)
opt Reviewer Leaves Feedback on Impl PR #42 (--stage fix)
Rev->>GitPR: Post code review comments on Impl PR #42
Dev->>Host: asdlc run #42 --stage fix
Host->>Runner: Ingest review comments via GraphQL
Runner->>AgentA: Modify source code & tests to address feedback
Runner->>Runner: Run asdlc verify (14 gates must pass)
Runner->>GitPR: Push fix commit + reply to thread + resolve thread
Runner-->>Host: Destroy container (--rm)
end
Dev->>GitPR: Human Approval & Merge Impl PR into main
4. Handling Review Comments Without a Terminal (--stage fix)
One common headache with automated coding is: what happens when human colleagues leave comments on a PR?
Reviewers often don’t click GitHub’s formal “Request Changes” button. They leave inline questions, suggest renames, or ask why a certain library was chosen.
When you run asdlc run #42 --stage fix, the container uses a 3-Tier Triage Engine:
flowchart TD
THREAD["Unresolved PR Review Thread (via GraphQL)"] --> CLASSIFY{"Review Comment Triage Engine"}
CLASSIFY -->|Tier 1: Concrete Suggestion / Bug| T1["<b>Tier 1: Code Fix</b><br/>โข Apply GitHub suggestion or code edit<br/>โข Re-run unit tests & asdlc verify<br/>โข Push fix commit & resolve thread"]
CLASSIFY -->|Tier 2: Technical Inquiry / Question| T2["<b>Tier 2: Technical Explanation</b><br/>โข Extract rationale from design.md<br/>โข Post inline reply in thread (Zero code edits)<br/>โข Resolve thread"]
CLASSIFY -->|Tier 3: Scope Creep / Out-of-Bounds| T3["<b>Tier 3: Scope Creep Deflection</b><br/>โข Identify boundary expansion<br/>โข Reply explaining spec scope bounds<br/>โข Defer to separate tracking issue"]
- Tier 1 (Code Fixes & Suggestions): If a reviewer points out a bug, suggests a variable rename, or provides a GitHub suggestion block (````suggestion`), the agent updates the code, reruns the test suite, verifies all gates pass, commits, and resolves the thread.
- Tier 2 (Technical Questions): If a reviewer asks: “Why did you use Redis here instead of Caffeine cache?”, the agent does not touch the code. It reads
design.md, writes a clear explanation as an inline reply, and resolves the thread. - Tier 3 (Scope Creep): If a reviewer asks: “Can we also add export-to-Excel in this PR?”, the agent checks the locked spec. It politely replies that this is out of scope for the current change, suggests opening a new ticket, and resolves the thread.
5. Dual-Track Symmetry: Zero Breaking Changes for IDE Users
A critical requirement was that container automation must not break the existing local workflow.
Developers who prefer working in IDEs (Cursor, Windsurf, or IntelliJ IDEA with AI plugins) or terminal CLI tools can keep working exactly as before. Both tracks share the same Git repo and OpenSpec files:
flowchart LR
subgraph Track1["Track 1: Local IDE Mode (Terminal)"]
direction TB
I_NEW["asdlc new (Interactive Q&A)"] --> I_LINT["asdlc lint"]
I_LINT --> I_PR1["asdlc pr (Spec PR)"]
I_PR1 --> I_APP["asdlc apply (Local TDD)"]
I_APP --> I_VER["asdlc verify"]
I_VER --> I_PR2["asdlc pr (Impl PR)"]
end
subgraph Shared["Single Source of Truth (Git + OpenSpec)"]
direction TB
SPEC["proposal.md & specs/**"]
LOCK["specSha Lock on main"]
CODE["Source Code & Tests"]
end
subgraph Track2["Track 2: Autonomous Container Mode (--container)"]
direction TB
C_DRAFT["asdlc run (Draft & Explore)"] --> C_GRILL["Draft PR Async Comment Q&A"]
C_GRILL --> C_PR1["Ready Spec PR"]
C_PR1 --> C_TDD["Container TDD (0444 Spec Lock)"]
C_TDD --> C_VER["Verify & Impl Review"]
C_VER --> C_PR2["Open Impl PR"]
end
I_NEW -.-> SPEC
C_DRAFT -.-> SPEC
I_PR1 -.-> LOCK
C_PR1 -.-> LOCK
I_APP -.-> CODE
C_TDD -.-> CODE
I_APP <-.->|"Bidirectional Handoff"| C_TDD
- Handoff between modes: You can draft a spec interactively in your IDE, and then run
asdlc run feat-auth --stage apply --containerto let the container handle the implementation tests in the background. - Zero-Checkout Remote Mode: You can also run changes against remote repositories without cloning them locally:
asdlc run "Add payment retry logic" \ --repo my-org/payment-service \ --from-issue JIRA-512 \ --stage spec \ --container
6. Key Container Engineering Trade-offs
Building an isolated container runner brought up several technical trade-offs:
1. Ephemeral Internal Clone vs. Host -v Mounting
The simplest approach is mounting the local folder with docker run -v $(pwd):/workspace. I decided against this:
- File I/O slow down: On Windows and macOS, bind mounts across virtualized file systems slow down build tools (npm, maven, pytest) by 5x to 10x.
- Workspace pollution: Linux containers write Linux binary caches and symlinks into the host folder, often creating Git dirty state or CRLF line ending issues on Windows.
- Decision: The container does a clean clone into internal ext4
/workspace. All code changes are pushed back via Git using temporarygh auth tokencredentials. The host directory stays completely clean.
2. Atomic Base Config vs. Recursive Config Mutation
How should container model settings combine with host AI configuration?
- Mutating configuration files or trying to merge custom proxies, headers, and API keys often silently corrupts credentials.
- Decision: Treat base configs as immutable units (copied as-is from host configs). If the container needs to use a specific model or provider (like an enterprise model instead of the developer’s default), pass it via runtime CLI flags and environment variables:
Runtime flags take priority natively, avoiding any file modification on disk.
claude -p --model "provider/model" "<prompt>"
3. Pure Data Staging vs. --net=host
How should auth credentials pass into the container?
- In corporate environments,
--net=hostis often blocked by security policies (CIS benchmarks). Also, OAuth localhost browser redirects fail across WSL2 and macOS VM boundaries. - Decision: No
--net=host. Authentication is extracted on the host and copied via single-directiondocker cpwith restricted permissions (UID 10001:10001, mode0600). The container runs inside a standard isolated bridge network.
4. Linux Kernel Read-Only Spec Lock (chmod 0444)
In Part 1, I introduced the spec-tamper rule to prevent agents from editing specs during implementation. In the container runner, this is enforced at the Linux kernel file system level:
flowchart TD
subgraph KERNEL ["Linux Kernel File System"]
FILE["openspec/changes/<name>/specs/spec.md"]
PERM["Ownership: root:root (0:0)<br/>Permissions: -r--r--r-- (0444)"]
AGENT["Agent Process<br/>(UID 10001:10001, No Sudo)"]
end
FILE --- PERM
AGENT -->|"Write / Edit Attempt"| PERM
PERM -->|Permission Denied| FAIL["๐ซ Hard Kernel Block (OS Level)"]
Before --stage apply starts, the container root script runs chown -R 0:0 specs/ and chmod -R 0444, then drops permissions permanently to unprivileged UID 10001. Even if an agent runs sed -i to change a test expectation in the spec, the Linux kernel rejects it with Permission denied.
7. Series Summary: The Full Journey
Across these three posts, here is the full arc of building a governance system for autonomous coding:
- Part 1: From AI-First + SDD to Agentic SDLC: Building a Dual-PR Governance Engine for Autonomous Coding: Why pairing AI-first coding with Spec-Driven Development (SDD) collapses under autonomous agent loops, and how Git-anchored Dual-PR governance fixes it.
- Part 2: Scaling Agentic SDLC: Designing an Adaptive Auto Mode for Autonomous Coding: Learning from Anthropic’s code review practices to build an L0โL3 adaptive mode that keeps routine tasks fast while protecting core APIs.
- Part 3: Beyond the IDE: Running Autonomous Agentic SDLC in Isolated Containers: Moving agents off developer laptops into isolated non-root containers with “3+1” stage execution, zero-checkout remote runs, and autonomous PR review comment triage.
AI agents are great at writing code quickly. But in real engineering teams, their value depends entirely on having hard, deterministic boundaries, clear contracts, and isolated review gates to keep production code safe.
๐ Links & RFCs
- Autonomous Pipeline RFC: Autonomous Container Pipeline Solution Proposal
agentic-sdlcRepository: https://github.com/akjamie/agentic-sdlc- OpenSpec Protocol: https://openspec.dev
- Anthropic Engineering Blog: How the Claude Code team does code review
ใ็่ฏ่ต่ต๏ผๆ็ไฝ้ฆใ
็่ฏ่ต่ต๏ผๆ็ไฝ้ฆ
ไฝฟ็จๅพฎไฟกๆซๆไบ็ปด็ ๅฎๆๆฏไป