Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
c43b533
docs(dark-factory): add Autonomous Agent Coding Pattern design + flow…
elamaran11 Jul 10, 2026
a6c4f62
docs(dark-factory): restyle diagrams to AWS dark-canvas keynote theme
elamaran11 Jul 10, 2026
740fa13
docs(dark-factory): correct LLM gateway to Bifrost (not LiteLLM)
elamaran11 Jul 10, 2026
c33374d
Merge remote-tracking branch 'origin/main' into dark-factory-autonomo…
elamaran11 Jul 10, 2026
6a02279
feat(agent-sandbox): add chart scaffold (Chart.yaml)
elamaran11 Jul 10, 2026
a0040be
feat(agent-sandbox): add chart values.yaml
elamaran11 Jul 10, 2026
072bae5
feat(agent-sandbox): add _helpers.tpl (labels, selectors, namespace)
elamaran11 Jul 10, 2026
c54170e
feat(agent-sandbox): add namespace with restricted Pod Security Standard
elamaran11 Jul 10, 2026
49a6f62
feat(agent-sandbox): add Kata RuntimeClasses (kata-clh, kata-qemu)
elamaran11 Jul 10, 2026
56348b7
feat(agent-sandbox): add coder SandboxTemplate with isolation invariants
elamaran11 Jul 10, 2026
b4ecdb2
feat(agent-sandbox): add default-deny egress NetworkPolicy for coders
elamaran11 Jul 10, 2026
b31c6eb
feat(agent-sandbox): add pool-manager RBAC (SA + namespaced Role)
elamaran11 Jul 10, 2026
0bcb9df
feat(agent-sandbox): add pool-manager CronJob (warm-buffer reconcile)
elamaran11 Jul 10, 2026
d564535
fix(agent-sandbox): implement pool-manager scale-to-zero + reap logic
elamaran11 Jul 10, 2026
6969a58
feat(agent-sandbox): add Sandbox operator install (SA/ClusterRole/Sta…
elamaran11 Jul 10, 2026
0ff631d
feat(agent-sandbox): register addon in the ApplicationSet catalog
elamaran11 Jul 10, 2026
a01e37d
feat(agent-sandbox): enable on spoke-dev overlay
elamaran11 Jul 10, 2026
22f498a
feat(agent-sandbox): enable on spoke-prod overlay (dormant capability)
elamaran11 Jul 10, 2026
d2ba408
docs(agent-sandbox): add chart README (templates, values, prerequisites)
elamaran11 Jul 10, 2026
c82f394
chore(agent-sandbox): disable on spoke-dev overlay (Auto Mode can't r…
elamaran11 Jul 10, 2026
e5015f9
chore(agent-sandbox): disable on spoke-prod overlay (Auto Mode can't …
elamaran11 Jul 10, 2026
cf69586
docs(dark-factory): document Kata-on-Auto-Mode blocker + integration …
elamaran11 Jul 10, 2026
ee17d0a
docs(dark-factory): add validated Kata-on-Auto-Mode design (§12a)
elamaran11 Jul 10, 2026
c5d020b
feat(agent-sandbox): add kata-deploy chart dependency (gated)
elamaran11 Jul 10, 2026
a365984
feat(agent-sandbox): add kata-deploy subchart values + kataDeploy toggle
elamaran11 Jul 10, 2026
4b24217
feat(agent-sandbox): add kata MNG launch-template nodeadm userData
elamaran11 Jul 10, 2026
91232af
feat(agent-sandbox): add eksctl manifest for the kata MNG
elamaran11 Jul 10, 2026
f2e790a
feat(agent-sandbox): add Terraform kata MNG + nested-virt launch temp…
elamaran11 Jul 10, 2026
ef7e7d7
fix(agent-sandbox): valid HCL for kata MNG Terraform (fmt + validate …
elamaran11 Jul 10, 2026
4fc6518
fix(agent-sandbox): drop kata-deploy subchart dep (delivered as separ…
elamaran11 Jul 10, 2026
324aa36
feat(agent-sandbox): register kata-deploy as separate gated ArgoCD app
elamaran11 Jul 10, 2026
62b746c
refactor(agent-sandbox): drop kata-deploy subchart values (now separa…
elamaran11 Jul 10, 2026
e19261f
docs(agent-sandbox): add nodepool README (provisioning + enablement s…
elamaran11 Jul 10, 2026
d4307aa
docs(dark-factory): note native SandboxWarmPool CRD (pool-manager lik…
elamaran11 Jul 10, 2026
47e04c4
fix(agent-sandbox): custom-AMI nodeadm needs apiServerEndpoint/CA/cid…
elamaran11 Jul 10, 2026
188d672
docs(agent-sandbox): vpc-cni addon required for self-managed MNG on A…
elamaran11 Jul 10, 2026
3bffdac
docs(dark-factory): live-test results — 4 fixes validated + kata-depl…
elamaran11 Jul 10, 2026
38e5b37
docs(agent-sandbox): document kata-deploy vs VPC-CNI Auto Mode blocke…
elamaran11 Jul 10, 2026
d9d02c8
docs(agent-sandbox): PROVEN — Kata micro-VM runs on EKS Auto Mode end…
elamaran11 Jul 10, 2026
4b6bb52
docs(dark-factory): §12a — full Kata-on-Auto-Mode validation confirmed
elamaran11 Jul 10, 2026
f68fad2
fix(agent-sandbox): SandboxTemplate has no volumeClaimTemplates (live…
elamaran11 Jul 10, 2026
e973b3a
feat(agent-sandbox): enable on spoke-dev overlay (Kata-on-Auto-Mode p…
elamaran11 Jul 10, 2026
2feb326
fix(agent-sandbox): correct kata-deploy OCI chart path (drop chartNam…
elamaran11 Jul 10, 2026
84173e4
fix(agent-sandbox): pool-manager image needs kubectl+jq (was aws-cli,…
elamaran11 Jul 10, 2026
341553f
feat(agent-sandbox): vendor upstream agent-sandbox v0.5.1 install man…
elamaran11 Jul 10, 2026
f36326e
refactor(agent-sandbox): drop hand-written operator (use vendored v0.…
elamaran11 Jul 10, 2026
14d4f8e
refactor(agent-sandbox): drop custom pool-manager (use native Sandbox…
elamaran11 Jul 10, 2026
b9e2df9
feat(agent-sandbox): add native SandboxWarmPool CR (replaces pool-man…
elamaran11 Jul 10, 2026
36833f6
feat(agent-sandbox): add kata-readiness DaemonSet (removes startup ta…
elamaran11 Jul 10, 2026
e468e19
fix(agent-sandbox): namespace PSS restricted→baseline (was blocking t…
elamaran11 Jul 10, 2026
7cef4f3
fix(agent-sandbox): use v1beta1 for SandboxTemplate + SandboxWarmPool
elamaran11 Jul 10, 2026
d7f5d32
feat(agent-sandbox): add operator as GitOps app + fix sync-wave ordering
elamaran11 Jul 10, 2026
7893a82
fix(agent-sandbox): package upstream operator as Helm chart (appset n…
elamaran11 Jul 10, 2026
acc7e44
fix(agent-sandbox): gate appsets to spoke-dev only (drop hub + prod)
elamaran11 Jul 10, 2026
b81c55e
fix(agent-sandbox): merge duplicate controller Deployment into one
elamaran11 Jul 10, 2026
218b1c6
feat(dark-factory): P1 orchestrator k8s lib — claim warm sandbox via …
elamaran11 Jul 10, 2026
46771c7
feat(dark-factory): P1 sticky PR status + PR creation lib
elamaran11 Jul 10, 2026
77895a1
feat(dark-factory): P1 coder-drive lib — files+env contract over the …
elamaran11 Jul 10, 2026
2351369
feat(dark-factory): P1 orchestrator server — claim→code→PR+live status
elamaran11 Jul 10, 2026
3cb3897
feat(dark-factory): P1 orchestrator package.json (@kubernetes/client-…
elamaran11 Jul 10, 2026
e02c771
feat(dark-factory): P1 orchestrator Dockerfile — non-root node:20-alpine
elamaran11 Jul 10, 2026
5d6a499
feat(dark-factory): P1 orchestrator RBAC — SandboxClaims only, namesp…
elamaran11 Jul 10, 2026
13b813d
feat(dark-factory): P1 orchestrator Deployment + Service (spoke-dev)
elamaran11 Jul 10, 2026
d54f3c2
feat(dark-factory): P1 trigger — GitHub Action on dark-factory label
elamaran11 Jul 10, 2026
47cb097
feat(dark-factory): P1 in-VM coder agent — honors the files+env contract
elamaran11 Jul 10, 2026
9904e71
feat(dark-factory): P1 coder Dockerfile — Kata-VM image, no baked creds
elamaran11 Jul 10, 2026
bfad46a
feat(dark-factory): P1 ArgoCD app — orchestrator pinned to spoke-dev …
elamaran11 Jul 10, 2026
14a41fa
docs(dark-factory): P1 README — components, request flow, claim contr…
elamaran11 Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/dark-factory.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Dark Factory (Flow B) trigger — issue labeled `dark-factory` → orchestrator.
#
# This is the P1 entrypoint (§4 step 1). It gates on the label, mints a
# short-TTL token scoped to this repo, and POSTs the run request to the
# orchestrator running on spoke-dev. The orchestrator (not this Action) holds
# the long-lived credentials and drives the sandbox; the Action's only job is
# to authenticate the trigger and hand over a short-lived token.
#
# The token here is the workflow's GITHUB_TOKEN (auto-expires when the run
# ends). For cross-repo / org installs, swap in a GitHub App token minted via
# actions/create-github-app-token — the orchestrator treats it the same way.
name: dark-factory

on:
issues:
types: [labeled]

# Least-privilege: the orchestrator opens the PR + updates the sticky comment
# using the token we pass, so it needs write on contents + PRs + issues.
permissions:
contents: write
pull-requests: write
issues: write

jobs:
dispatch:
# Only fire when the `dark-factory` label is the one that was added.
if: github.event.label.name == 'dark-factory'
runs-on: ubuntu-latest
steps:
- name: Acknowledge on the issue
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '🏭 Dark Factory accepted this issue — claiming a sandbox on spoke-dev…',
});

- name: Dispatch to orchestrator
env:
# The orchestrator's ingress URL (private ALB / VPN-reachable).
# Configure as a repo/org variable; kept out of the workflow so the
# endpoint isn't hard-coded.
ORCHESTRATOR_URL: ${{ vars.DARK_FACTORY_ORCHESTRATOR_URL }}
# Shared secret so the orchestrator only accepts trusted triggers.
DISPATCH_TOKEN: ${{ secrets.DARK_FACTORY_DISPATCH_TOKEN }}
GH_TOKEN: ${{ github.token }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
REPO: ${{ github.repository }}
BASE: ${{ github.event.repository.default_branch }}
run: |
set -euo pipefail
if [ -z "${ORCHESTRATOR_URL:-}" ]; then
echo "::error::DARK_FACTORY_ORCHESTRATOR_URL is not set — cannot dispatch."
exit 1
fi
# Build the JSON payload safely (jq handles escaping of title/body).
payload="$(jq -n \
--arg issue "$ISSUE_NUMBER" \
--arg repo "$REPO" \
--arg title "$ISSUE_TITLE" \
--arg body "$ISSUE_BODY" \
--arg base "$BASE" \
--arg ghToken "$GH_TOKEN" \
'{issue: ($issue|tonumber), repo: $repo, title: $title, body: $body, base: $base, ghToken: $ghToken}')"
curl -fsS -X POST "$ORCHESTRATOR_URL/run" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${DISPATCH_TOKEN:-}" \
-d "$payload"
echo "Dispatched issue #$ISSUE_NUMBER to the Dark Factory orchestrator."
499 changes: 499 additions & 0 deletions docs/dark-factory/README.md

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions docs/dark-factory/diagrams/flow-a-sandbox-capability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Flow A — Agent Sandbox Capability (permanent platform feature)

The **Agent Sandbox** capability ships as a first-class agent-platform GitOps addon. When the
platform is deployed, it stands up the Kata (Cloud Hypervisor) micro-VM runtime, the `Sandbox`
CRD + operator, and a **warm pool** of pre-provisioned, hardware-isolated sandboxes kept ready by
a pool-manager controller. This is independent of the Dark Factory — it is the reusable isolation
substrate any agent workload can claim.

---

## A.1 — Capability architecture

```mermaid
%%{init: {'theme':'base','themeVariables':{
'fontFamily':'Amazon Ember, Helvetica, Arial, sans-serif','fontSize':'14px',
'primaryColor':'#2E3E50','primaryBorderColor':'#5F7185','primaryTextColor':'#FFFFFF',
'lineColor':'#8B99A7','clusterBkg':'#26374A','clusterBorder':'#5F7185',
'tertiaryColor':'#1B2733'}}}%%
flowchart TB
classDef canvas fill:#1B2733,stroke:#1B2733,color:#FFFFFF;
classDef band fill:#26374A,stroke:#5F7185,color:#E8EDF2,stroke-width:1px;
classDef node fill:#2E4257,stroke:#7C8FA3,color:#FFFFFF,stroke-width:1.2px;
classDef accent fill:#FF9900,stroke:#EC7211,color:#161E2D,stroke-width:1.5px;
classDef warm fill:#22384A,stroke:#FF9900,color:#FFFFFF,stroke-width:1.4px;
classDef muted fill:#243244,stroke:#415266,color:#AEB8C4,stroke-width:1px;

subgraph CANVAS[" "]
direction TB

subgraph GITOPS["🚀 GitOps enablement · ArgoCD app-of-apps"]
direction LR
FLAG["enable_agent_sandbox: true<br/>overlays/environments/dev,prod"]:::accent
APPSET["ApplicationSet<br/>cluster-generator<br/>sync-wave −1 → 1"]:::node
FLAG --> APPSET
end

subgraph CLUSTER["☸️ spoke-dev &amp; spoke-prod · capability on both"]
direction TB

subgraph OPER["🧩 Sandbox control plane"]
direction LR
CRD["Sandbox CRD<br/>agents.x-k8s.io/v1alpha1<br/>+ SandboxTemplate + SandboxClaim"]:::node
OPCTL["agent-sandbox<br/>operator"]:::node
POOL["pool-manager<br/>keeps N idle · refills · shrinks"]:::accent
CRD --- OPCTL --- POOL
end

subgraph WARM["🔥 Warm pool · target buffer = 2–3 idle"]
direction LR
S1(["idle · sandbox-1"]):::warm
S2(["idle · sandbox-2"]):::warm
S3(["idle · sandbox-3"]):::warm
end

subgraph KATA["🛡️ Kata micro-VM runtime · hardware isolation"]
direction LR
RC["RuntimeClasses<br/>kata-clh · kata-qemu · kata-fc"]:::node
KD["kata-deploy<br/>DaemonSet"]:::node
NP["Karpenter pools<br/>kata-nested c8i/m8i · kata-metal"]:::node
RC --- KD --- NP
end

MODEL["🧠 Bifrost LLM gateway<br/>bifrost.bifrost.svc:8080 → Bedrock"]:::node
end
end

APPSET ==> OPER
APPSET ==> KATA
POOL ==> WARM
WARM -.-> KATA
WARM -.-> MODEL

class CANVAS canvas
class GITOPS,CLUSTER,OPER,WARM,KATA band
linkStyle default stroke:#8B99A7,stroke-width:1.4px;
linkStyle 7 stroke:#FF9900,stroke-width:2px;
```

---

## A.2 — Warm-pool cycling (claim ↔ refill ↔ shrink)

The pool-manager keeps a steady buffer of idle sandboxes so a consumer (e.g. the Dark Factory)
binds to a **ready** VM instantly instead of paying cold-start. Idle sandboxes use the `Sandbox`
`replicas: 0/1` **scale subresource**, so "idle" is cheap.

```mermaid
%%{init: {'theme':'base','themeVariables':{
'fontFamily':'Amazon Ember, Helvetica, Arial, sans-serif','fontSize':'14px',
'primaryColor':'#2E4257','primaryBorderColor':'#7C8FA3','primaryTextColor':'#FFFFFF',
'lineColor':'#8B99A7','labelColor':'#E8EDF2'}}}%%
stateDiagram-v2
direction LR
[*] --> Provisioning : pool below target
Provisioning --> Idle : VM booted · replicas=1 · unclaimed
Idle --> Claimed : SandboxClaim binds
Claimed --> Idle : claim released · recyclable
Idle --> Paused : idle TTL · scale replicas=0 · PVC kept
Paused --> Idle : demand returns · scale replicas=1
Claimed --> Retired : consumer done · delete Sandbox+PVC
Idle --> Retired : pool above target · shrink
Retired --> [*]

note right of Claimed
On CLAIM the pool-manager provisions
a REFILL so the buffer stays at target.
end note
note right of Retired
On RELEASE, if the pool is above target,
the extra idle sandbox is removed.
end note
```
183 changes: 183 additions & 0 deletions docs/dark-factory/diagrams/flow-b-dark-factory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Flow B — Dark Factory (issue → PR → merge → teardown)

A GitHub issue is a **spec**. The factory claims a warm sandbox on spoke-dev, a pluggable coding
assistant implements + tests the change, the orchestrator runs independent verification (holdout
gate + AWS frontier agents), a PR is opened with a **live-updating** status, a human approves on
results, and everything is torn down on merge. Autonomy **Level 3**: the human's only job is to
approve the merge.

---

## B.1 — End-to-end pipeline (lights-off assembly line)

```mermaid
%%{init: {'theme':'base','themeVariables':{
'fontFamily':'Amazon Ember, Helvetica, Arial, sans-serif','fontSize':'13.5px',
'primaryColor':'#2E4257','primaryBorderColor':'#7C8FA3','primaryTextColor':'#FFFFFF',
'lineColor':'#8B99A7','clusterBkg':'#26374A','clusterBorder':'#5F7185'}}}%%
flowchart LR
classDef canvas fill:#1B2733,stroke:#1B2733,color:#FFFFFF;
classDef band fill:#26374A,stroke:#5F7185,color:#E8EDF2,stroke-width:1px;
classDef node fill:#2E4257,stroke:#7C8FA3,color:#FFFFFF,stroke-width:1.2px;
classDef accent fill:#FF9900,stroke:#EC7211,color:#161E2D,stroke-width:1.5px;
classDef human fill:#22384A,stroke:#FF9900,color:#FFFFFF,stroke-width:1.4px;
classDef gate fill:#2E4257,stroke:#FF9900,color:#FFFFFF,stroke-width:1.4px;

subgraph CANVAS[" "]
direction LR

START(["🧾 Issue opened<br/>label: dark-factory"]):::accent

subgraph T["① Trigger"]
GHA["⚙️ GitHub Action<br/>gate on label<br/>mint short-TTL token"]:::node
end

subgraph O["② Orchestrate · spoke-dev · trusted (holds AWS IAM)"]
ORCH["🎛️ Orchestrator<br/>key = issue-id<br/>claim · drive · report"]:::node
CLAIM["🔒 SandboxClaim<br/>bind warm sandbox · pool refills"]:::node
ORCH --> CLAIM
end

subgraph SBX["③ Code + self-test · Kata micro-VM · isolated (no cloud creds)"]
CODER["🤖 Pluggable coder<br/>Claude Code | Kiro<br/>reads SPEC.md"]:::node
IMPL["✍️ implement<br/>branch df/issue-N"]:::node
BUILD["🔧 build + unit tests<br/>until green"]:::node
CODER --> IMPL --> BUILD
end

subgraph V["④ Independent verification · orchestrator-driven (NOT the coder)"]
HOLD["🎯 Holdout gate<br/>hidden scenarios · different-model judge<br/>+ executable tests · ≥90%"]:::gate
SEC["🛡️ AWS Security Agent"]:::node
DEV["🚀 AWS DevOps Agent"]:::node
HOLD -.-> SEC -.-> DEV
end

subgraph P["⑤ Pull Request · live status"]
PR["🔀 PR opened<br/>sticky comment ⏳→✅<br/>results + findings + logs"]:::node
end

subgraph H["⑥ Human · Level 3"]
REVIEW{"Approve?<br/>reviews RESULTS"}:::human
COMMENT["💬 PR comment<br/>resume sandbox · iterate<br/>bounded N rounds"]:::human
end

subgraph TD["⑦ Merge + teardown"]
MERGE["✅ merge PR"]:::accent
DESTROY["🧹 delete sandbox + PVC<br/>+ ephemeral test infra + eval job"]:::node
MERGE --> DESTROY --> DONE(["🏁 done"]):::node
end

START ==> GHA ==> ORCH
CLAIM ==> CODER
BUILD ==> HOLD
DEV ==> PR
PR ==> REVIEW
REVIEW -->|changes requested| COMMENT
COMMENT -.->|new instruction| CODER
REVIEW ==>|approve| MERGE
end

class CANVAS canvas
class T,O,SBX,V,P,H,TD band
linkStyle default stroke:#8B99A7,stroke-width:1.4px;
linkStyle 7,8,9,10,11,12,15 stroke:#FF9900,stroke-width:2px;
```

---

## B.2 — Detailed sequence (who does what, and the trust boundary)

```mermaid
%%{init: {'theme':'base','themeVariables':{
'fontFamily':'Amazon Ember, Helvetica, Arial, sans-serif','fontSize':'13px',
'primaryColor':'#2E4257','primaryBorderColor':'#7C8FA3','primaryTextColor':'#FFFFFF',
'actorBkg':'#2E4257','actorBorder':'#FF9900','actorTextColor':'#FFFFFF',
'signalColor':'#8B99A7','signalTextColor':'#E8EDF2','lineColor':'#8B99A7',
'noteBkg':'#FF9900','noteTextColor':'#161E2D','noteBorderColor':'#EC7211',
'loopTextColor':'#E8EDF2','sequenceNumberColor':'#161E2D'}}}%%
sequenceDiagram
autonumber
actor Dev as 🧑‍💻 Human
participant GH as 🐙 GitHub
participant GHA as ⚙️ Action
participant ORCH as 🎛️ Orchestrator (AWS IAM)
participant POOL as 🔥 Warm pool
box rgb(34,56,74) 🛡️ Kata micro-VM · isolated · no cloud creds
participant SBX as 🤖 Coder sandbox
end
participant EVAL as 🎯 Holdout evaluator
participant AWS as ☁️ AWS Security / DevOps Agents

Dev->>GH: open issue (label: dark-factory)
GH->>GHA: issues.labeled event
GHA->>ORCH: POST /run (issue #, repo, short-TTL token)
ORCH->>POOL: SandboxClaim (key = issue-id)
POOL-->>ORCH: bound warm sandbox (instant)
Note over POOL: pool-manager provisions a REFILL

ORCH->>SBX: write SPEC.md + checkout df/issue-N
activate SBX
SBX->>SBX: implement · build · unit tests (until green)
SBX-->>ORCH: result.json (diff + logs)
deactivate SBX

Note over ORCH,EVAL: Verification is INDEPENDENT of the coder<br/>(coder never sees or edits the holdout)
ORCH->>EVAL: run hidden scenarios vs diff
EVAL-->>ORCH: satisfaction % (different-model judge + real tests)
ORCH->>AWS: invoke Security + DevOps agents on diff
AWS-->>ORCH: findings (advisory)

ORCH->>GH: open PR + sticky status comment (⏳→✅)
GH-->>Dev: review request (results, not code)

loop bounded iterations (max N)
Dev->>GH: PR comment (change requested)
GH->>ORCH: comment webhook
ORCH->>POOL: resume sandbox (scale 0→1, same PVC)
ORCH->>SBX: apply comment as new instruction
activate SBX
SBX-->>ORCH: push update
deactivate SBX
ORCH->>GH: update sticky status
end

Dev->>GH: ✅ approve + merge
GH->>ORCH: pull_request closed+merged
ORCH->>POOL: release + delete sandbox + PVC
ORCH->>ORCH: delete ephemeral test infra + eval job
Note over ORCH: Reaper CronJob reaps any abandoned run
```

---

## B.3 — Live PR status (the sticky comment the human watches)

The orchestrator maintains **one** PR comment, edited in place as each stage completes — no comment
spam, one canonical surface (the pattern Copilot/Devin/Factory converge on).

```mermaid
%%{init: {'theme':'base','themeVariables':{
'fontFamily':'Amazon Ember, Helvetica, Arial, sans-serif','fontSize':'13px',
'primaryColor':'#2E4257','primaryBorderColor':'#7C8FA3','primaryTextColor':'#FFFFFF',
'lineColor':'#8B99A7','clusterBkg':'#26374A','clusterBorder':'#5F7185'}}}%%
flowchart TB
classDef canvas fill:#1B2733,stroke:#1B2733,color:#FFFFFF;
classDef done fill:#22384A,stroke:#3FB950,color:#FFFFFF,stroke-width:1.3px;
classDef now fill:#FF9900,stroke:#EC7211,color:#161E2D,stroke-width:1.5px;
classDef wait fill:#243244,stroke:#5F7185,color:#AEB8C4,stroke-width:1px;

subgraph CANVAS["🏭 Dark Factory — issue #42"]
direction TB
A["✅ Claimed sandbox (spoke-dev) · 12:01"]:::done
B["✅ Branch df/issue-42 · 12:01"]:::done
C["✅ Implement · 12:04"]:::done
D["✅ Build + unit tests · 12:07 · 📄 log"]:::done
E["⏳ Security Agent…"]:::now
F["⬜ DevOps Agent"]:::wait
G["⬜ Holdout gate (0/12)"]:::wait
I["⬜ PR ready for review"]:::wait
A --> B --> C --> D --> E --> F --> G --> I
end
class CANVAS canvas
linkStyle default stroke:#5F7185,stroke-width:1.2px;
```
Loading