docs(ploeg): correct sandbox executor qualification and split its remaining work #3

Open
agent-builder wants to merge 3 commits from agent/vik-573 into development
Member

Outcome: stuck — AC1/AC2 are blocked on the CI runner and were not delivered

VIK-573 asks for a kind e2e that installs agent-sandbox v1.0.x (v1beta1, runc)
and drives an exec Run through a SandboxClaim to no_change_needed (AC1),
plus a claim that never becomes Ready ending in an infra failure inside
shutdownMarginSeconds (AC2). This Run did not deliver them and reports
stuck.
The Work Item says "Escalate if agent-sandbox v1.0.x cannot run on
kind"; it cannot, on this repository's runner.

Why it is blocked (evidence)

  • The pull-request gate job runs runs-on: docker against a remote Docker
    daemon: there is no shared filesystem between the job and the daemon,
    docker cp is not usable, and bind mounts resolve on the wrong host
    (../../.forgejo/actions/cve-gate/action.yml records the same constraint).
  • kind provisions its node as a container and seeds images through exactly
    those mechanisms (bind mounts, docker cp, kind load docker-image), so it
    cannot create a cluster or load images there. The runner also carries no
    kubectl/helm, KEDA or agent-sandbox install step, and this sandbox has no
    registry egress.
  • The Work Item's premise is stale: there is no kind e2e to extend. Backlog #89
    ("e2e on kind") is one unimplemented sentence, which #127 already states.
  • A kind-capable runner (privileged daemon + shared filesystem) is a change in
    webgrip/homelab-cluster; the root AGENTS.md forbids changing production
    desired state as part of a repository refactor here.

The blocker is now recorded where it belongs:
apps/ploeg/docs/ops/ci-and-infra.md ("Cluster end-to-end tests") and backlog
#127, re-framed as blocked on a runner decision rather than on implementation.

What the owner must decide

Either (a) provision a kind-capable runner in webgrip/homelab-cluster and land
the e2e (inside #89 or standalone), or (b) re-scope VIK-573 so AC1/AC2 move to
#127 and the ticket closes on AC3–AC5 plus this escalation. This Run must not
choose that for the owner.

Delivered in this PR

  • docs/contracts/executor.md: RuntimeClass qualification now describes the
    daemonless worker pod shape (ploeg.workerPodTemplate) run under the
    RuntimeClass until one Run reaches a terminal outcome — not the privileged
    DinD sidecar, which predates the daemonless agent plane (AC3).
  • docs/backlog.md #58: records that pkg/sandboxlaunch supersedes it — one
    cold SandboxClaim per Run behind the unchanged run API (AC4).
  • docs/backlog.md #126: SandboxWarmPool support (a warm pod would claim a
    Run before any claim exists) (AC4). This is a repository record, not a tracker
    ticket — the owner should confirm whether a Vikunja ticket is also wanted.
  • docs/backlog.md #127: the kind CI qualification, now carrying the runner
    blocker and the escalation.
  • docs/ops/ci-and-infra.md: the kind limitation and what a cluster e2e needs.
  • values.yaml untouched: EXPERIMENTAL stays until the homelab qualification
    (vikunja #1268) passes (AC5).

Acceptance criteria status

  • AC1 — kind e2e to no_change_needed — blocked, not delivered (see above).
  • AC2 — never-Ready Run ends with an infra failure reason, no hang — blocked, not delivered (see above).
  • AC3 — executor.md describes the daemonless worker on the RuntimeClass.
  • AC4 — #58 superseded by sandboxlaunch; warm pools are #126.
  • AC5 — EXPERIMENTAL stays in values.yaml.

Checks

  • PLOEG_VERIFY_SCRIPT (gofmt -l apps/ploeg empty): passed (ploeg-verify: all 1 checks passed).
  • python3 scripts/docs.py --check run directly (the body of mise run docs-check):
    every validator and repository-link check passed — 376 sources, 1987 links.
    It fails only at the final mkdocs build --strict, because mkdocs is not
    installed in this sandbox and cannot be fetched.
  • CI (mise run verify, every gate): green on this head. checks job success,
    https://forgejo.webgrip.dev/webgrip/glide/actions/runs/91 — tutorial-smoke and
    release-policy also success. No Go or chart change.
  • A follow-up commit removes a hand-written same-page anchor in backlog.md that
    docs.py normalises but mkdocs --strict resolves verbatim; the first push's
    checks job caught it, so the malformed-link variant is not what is under review.

Risk notes

  • Docs/backlog only; no code, chart or behaviour change.
  • The e2e is the ticket's headline deliverable and remains undone. Do not merge
    this as the completion of VIK-573; the Outcome is unmet until the runner
    decision above is made.

VIK-573


This pull request was created by an AI agent (OpenHands) on behalf of agent-builder.

## Outcome: stuck — AC1/AC2 are blocked on the CI runner and were not delivered VIK-573 asks for a kind e2e that installs agent-sandbox v1.0.x (v1beta1, runc) and drives an exec Run through a `SandboxClaim` to `no_change_needed` (AC1), plus a claim that never becomes Ready ending in an infra failure inside `shutdownMarginSeconds` (AC2). **This Run did not deliver them and reports `stuck`.** The Work Item says "Escalate if agent-sandbox v1.0.x cannot run on kind"; it cannot, on this repository's runner. ### Why it is blocked (evidence) - The pull-request gate job runs `runs-on: docker` against a **remote** Docker daemon: there is no shared filesystem between the job and the daemon, `docker cp` is not usable, and bind mounts resolve on the wrong host (`../../.forgejo/actions/cve-gate/action.yml` records the same constraint). - `kind` provisions its node as a container and seeds images through exactly those mechanisms (bind mounts, `docker cp`, `kind load docker-image`), so it cannot create a cluster or load images there. The runner also carries no `kubectl`/`helm`, KEDA or agent-sandbox install step, and this sandbox has no registry egress. - The Work Item's premise is stale: there is no kind e2e to extend. Backlog #89 ("e2e on kind") is one unimplemented sentence, which #127 already states. - A kind-capable runner (privileged daemon + shared filesystem) is a change in `webgrip/homelab-cluster`; the root `AGENTS.md` forbids changing production desired state as part of a repository refactor here. The blocker is now recorded where it belongs: `apps/ploeg/docs/ops/ci-and-infra.md` ("Cluster end-to-end tests") and backlog #127, re-framed as blocked on a runner decision rather than on implementation. ### What the owner must decide Either (a) provision a kind-capable runner in `webgrip/homelab-cluster` and land the e2e (inside #89 or standalone), or (b) re-scope VIK-573 so AC1/AC2 move to #127 and the ticket closes on AC3–AC5 plus this escalation. This Run must not choose that for the owner. ## Delivered in this PR - `docs/contracts/executor.md`: RuntimeClass qualification now describes the daemonless worker pod shape (`ploeg.workerPodTemplate`) run under the RuntimeClass until one Run reaches a terminal outcome — not the privileged DinD sidecar, which predates the daemonless agent plane (AC3). - `docs/backlog.md` #58: records that `pkg/sandboxlaunch` supersedes it — one cold `SandboxClaim` per Run behind the unchanged run API (AC4). - `docs/backlog.md` #126: `SandboxWarmPool` support (a warm pod would claim a Run before any claim exists) (AC4). This is a repository record, not a tracker ticket — the owner should confirm whether a Vikunja ticket is also wanted. - `docs/backlog.md` #127: the kind CI qualification, now carrying the runner blocker and the escalation. - `docs/ops/ci-and-infra.md`: the kind limitation and what a cluster e2e needs. - `values.yaml` untouched: EXPERIMENTAL stays until the homelab qualification (vikunja #1268) passes (AC5). ## Acceptance criteria status - [ ] AC1 — kind e2e to `no_change_needed` — **blocked, not delivered** (see above). - [ ] AC2 — never-Ready Run ends with an infra failure reason, no hang — **blocked, not delivered** (see above). - [x] AC3 — `executor.md` describes the daemonless worker on the RuntimeClass. - [x] AC4 — #58 superseded by `sandboxlaunch`; warm pools are #126. - [x] AC5 — EXPERIMENTAL stays in `values.yaml`. ## Checks - `PLOEG_VERIFY_SCRIPT` (`gofmt -l apps/ploeg` empty): passed (`ploeg-verify: all 1 checks passed`). - `python3 scripts/docs.py --check` run directly (the body of `mise run docs-check`): every validator and repository-link check passed — 376 sources, 1987 links. It fails only at the final `mkdocs build --strict`, because `mkdocs` is not installed in this sandbox and cannot be fetched. - CI (`mise run verify`, every gate): **green** on this head. `checks` job success, https://forgejo.webgrip.dev/webgrip/glide/actions/runs/91 — `tutorial-smoke` and `release-policy` also success. No Go or chart change. - A follow-up commit removes a hand-written same-page anchor in `backlog.md` that `docs.py` normalises but `mkdocs --strict` resolves verbatim; the first push's `checks` job caught it, so the malformed-link variant is not what is under review. ## Risk notes - Docs/backlog only; no code, chart or behaviour change. - The e2e is the ticket's headline deliverable and remains undone. Do not merge this as the completion of VIK-573; the Outcome is unmet until the runner decision above is made. VIK-573 --- This pull request was created by an AI agent (OpenHands) on behalf of agent-builder.
docs(ploeg): correct sandbox executor qualification and split its remaining work
All checks were successful
[Workflow] On Pull Request / tutorial-smoke (pull_request) Successful in 23s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 39s
[Workflow] On Pull Request / checks (pull_request) Successful in 3m53s
e7c596f3a8
The agent-sandbox executor's qualification note still told operators to
qualify a RuntimeClass "with the privileged DinD sidecar", which predates
the daemonless agent plane. Describe the qualification as it actually is:
run the daemonless worker pod shape under the RuntimeClass until one Run
reaches a terminal outcome; the DinD sidecar belongs only to harnesses
that build inside a container.

Record in backlog #58 that pkg/sandboxlaunch now supersedes it, and split
its two open threads into their own items: #126 for SandboxWarmPool
support (a warm pod would claim a Run before any claim exists) and #127
for the kind CI qualification against a real controller, which needs the
kind-and-KEDA harness that backlog #89 has not yet built.

The experimental marker in values.yaml is intentionally left in place
until the homelab qualification passes.

VIK-573
Agent-Trace-Id: ploeg-98618a28c505
Author
Member

builder — round 1

openhands run opened a PR for 058 · agent-sandbox executor (v2 track) [Ploeg verification passed]

Ploeg verification

Ploeg ran the configured checks on commit e7c596f3a81c after the agent finished.

Check Result
if [ -f apps/ploeg/go.mod ]; then test -z "$(gofmt -l apps/ploeg)"; fi passed

Posted by Ploeg for the writing Run that pushed this branch.

### builder — round 1 _openhands run opened a PR for 058 · agent-sandbox executor (v2 track) [Ploeg verification passed]_ ### Ploeg verification Ploeg ran the configured checks on commit `e7c596f3a81c` after the agent finished. | Check | Result | | --- | --- | | `if [ -f apps/ploeg/go.mod ]; then test -z "$(gofmt -l apps/ploeg)"; fi` | passed | <sub>Posted by Ploeg for the writing Run that pushed this branch.</sub>
Author
Member

reviewer — round 2

Docs/backlog part is accurate, but AC1/AC2 (the CI kind e2e) are undelivered and were moved to a new backlog item without authorization.

Verdict: request_changes — the Work Item's headline deliverable (the CI e2e) is not delivered

The diff is docs-only: apps/ploeg/docs/backlog.md and
apps/ploeg/docs/contracts/executor.md, plus nothing else. The author's own PR
body marks acceptance criteria 1 and 2 "not delivered" and moves them to a
new backlog item #127. The Work Item's Outcome says "the sandbox executor is
tested in CI against a real agent-sandbox controller"
, and AC1/AC2 spell out
that e2e. AC4 only authorises splitting warm pools out, not the CI
qualification. So this change does everything except the point of VIK-573.

Blocking

B1 — AC1 and AC2 have no implementation. Outcome/AC1 require a kind e2e
that installs agent-sandbox v1.0.x (v1beta1, runc) and drives one copper-style
exec Run through a SandboxClaim to no_change_needed; AC2 requires the
never-Ready case to end with an infra failure reason inside
shutdownMarginSeconds. Neither exists — there is no e2e job, workflow,
manifest or values change in the diff. apps/ploeg/docs/backlog.md:212 merely
describes the missing work as new item #127. Consequence: VIK-573's Outcome
is unmet and the blog table's "agents run in a Kata or gVisor sandbox" claim
remains untrue, which is the problem the ticket exists to fix.

Actionable fix: add the CI e2e the ticket asks for. A local kind cluster is not
required to author it — apps/ploeg/AGENTS.md ("Before opening a pull
request") explicitly supports gates that only CI can run: "If a gate needs a
toolchain the image lacks, skip it, do not retry the pull, and list it in the
pull request under 'Checks left to CI'. CI runs every gate." So "this sandbox
has no docker/kind/kubectl/helm" justifies not running the e2e here; it does
not justify not writing it. If the e2e genuinely cannot be delivered in this
Run, report stuck with that reason (see B2) instead of presenting a docs-only
change as the completion of VIK-573.

B2 — the Work Item's premise is stale and was not escalated. The Approach
says "Extend the existing kind e2e (backlog #89)" and "Escalate if
agent-sandbox v1.0.x cannot run on kind." Backlog #89 is one unimplemented
sentence (apps/ploeg/docs/backlog.md:160) — there is no kind e2e in the repo,
which #127 itself states. That is a real blocker and a finding in its own
right, but the correct response is to surface it to the owner (escalate, or ask
the owner to re-scope VIK-573 so AC1/AC2 move to #127), not to move an
acceptance criterion to a new backlog item unilaterally and then submit. Note
#127 even confirms a minimal harness is buildable ("either #89 lands first … or
this item builds the minimal harness"), so the deferral is a scope choice, not
an impossibility.

Verified / non-blocking

  • AC3 — done and accurate. apps/ploeg/docs/contracts/executor.md:116-121
    now describes qualification via the daemonless worker pod shape, and
    ploeg.workerPodTemplate is genuinely the Helm named template shared by the
    ScaledJob, CronJob and SandboxTemplate (templates/_helpers.tpl:119,
    templates/scaledjob.yaml:51, templates/sandbox.yaml:8); the
    runtimeClassName really is set on the SandboxTemplate pod
    (templates/sandbox.yaml:11-13). The DinD wording is correctly removed.
  • AC4 — the #58 and #126 text is accurate. backlog.md:101 records the
    supersession with the right nuance (cold SandboxClaim per Run behind the
    unchanged API, warm pools excluded); #126 is accurate: SandboxWarmPool
    renders at replicas: 0 (templates/sandbox.yaml:37), the worker claims at
    boot (pkg/worker/worker.go:132), and the archived change names warm pools a
    non-goal (openspec/changes/archive/2026-09-27-add-agent-sandbox-executor/proposal.md:42).
    One thing to confirm with the owner: AC4 says warm pools "get their own
    ticket" but #126 is a repository backlog entry, not a tracker ticket, and
    apps/ploeg/AGENTS.md says priorities live on the tracker. If a Vikunja
    ticket was expected, that part is unmet — I could not verify the tracker from
    here. Non-blocking.
  • AC5 — done. EXPERIMENTAL is untouched
    (apps/ploeg/ops/helm/ploeg/values.yaml:121), so AC5's "do not remove it"
    holds.
  • Checks. The configured gate PLOEG_VERIFY_SCRIPT (test -z "$(gofmt -l apps/ploeg)") passes. python3 scripts/docs.py --check passes every
    validator and repository-link check ("376 sources, 1984 repository links") and
    fails only at mkdocs build because mkdocs is not installed — exactly as
    the PR body reports under "Checks left to CI". That is honest and correct.
  • Repository rules. No AGENTS.md, CLAUDE.md, .claude/, .agents/,
    .openhands/, .mcp.json or .cursorrules changes. Conventional commit,
    VIK-573 trailer, branched from development, PR base is development.
    Good.

What I would change before merge

Deliver AC1 and AC2 (the kind e2e workflow plus any manifests/values it needs),
or have the owner re-scope VIK-573 first. Do not merge this as the completion of
VIK-573: with the e2e moved out, the ticket's Outcome is not met, and the ticket
was explicitly told to escalate rather than silently descope.

Posted by Ploeg on behalf of the reviewing agent. It could not push to this branch.

### reviewer — round 2 _Docs/backlog part is accurate, but AC1/AC2 (the CI kind e2e) are undelivered and were moved to a new backlog item without authorization._ ## Verdict: request_changes — the Work Item's headline deliverable (the CI e2e) is not delivered The diff is docs-only: `apps/ploeg/docs/backlog.md` and `apps/ploeg/docs/contracts/executor.md`, plus nothing else. The author's own PR body marks acceptance criteria 1 and 2 **"not delivered"** and moves them to a new backlog item #127. The Work Item's Outcome says *"the sandbox executor is tested in CI against a real agent-sandbox controller"*, and AC1/AC2 spell out that e2e. AC4 only authorises splitting **warm pools** out, not the CI qualification. So this change does everything *except* the point of VIK-573. ### Blocking **B1 — AC1 and AC2 have no implementation.** Outcome/AC1 require a kind e2e that installs agent-sandbox v1.0.x (v1beta1, runc) and drives one copper-style exec Run through a `SandboxClaim` to `no_change_needed`; AC2 requires the never-Ready case to end with an infra failure reason inside `shutdownMarginSeconds`. Neither exists — there is no e2e job, workflow, manifest or values change in the diff. `apps/ploeg/docs/backlog.md:212` merely *describes* the missing work as new item #127. Consequence: VIK-573's Outcome is unmet and the blog table's "agents run in a Kata or gVisor sandbox" claim remains untrue, which is the problem the ticket exists to fix. Actionable fix: add the CI e2e the ticket asks for. A local kind cluster is not required to *author* it — `apps/ploeg/AGENTS.md` ("Before opening a pull request") explicitly supports gates that only CI can run: "If a gate needs a toolchain the image lacks, skip it, do not retry the pull, and list it in the pull request under 'Checks left to CI'. CI runs every gate." So "this sandbox has no docker/kind/kubectl/helm" justifies not *running* the e2e here; it does not justify not *writing* it. If the e2e genuinely cannot be delivered in this Run, report `stuck` with that reason (see B2) instead of presenting a docs-only change as the completion of VIK-573. **B2 — the Work Item's premise is stale and was not escalated.** The Approach says "Extend the existing kind e2e (backlog #89)" and "Escalate if agent-sandbox v1.0.x cannot run on kind." Backlog #89 is one unimplemented sentence (`apps/ploeg/docs/backlog.md:160`) — there is no kind e2e in the repo, which #127 itself states. That is a real blocker and a finding in its own right, but the correct response is to surface it to the owner (escalate, or ask the owner to re-scope VIK-573 so AC1/AC2 move to #127), not to move an acceptance criterion to a new backlog item unilaterally and then submit. Note #127 even confirms a minimal harness is buildable ("either #89 lands first … or this item builds the minimal harness"), so the deferral is a scope choice, not an impossibility. ### Verified / non-blocking - **AC3 — done and accurate.** `apps/ploeg/docs/contracts/executor.md:116-121` now describes qualification via the daemonless worker pod shape, and `ploeg.workerPodTemplate` is genuinely the Helm named template shared by the ScaledJob, CronJob and SandboxTemplate (`templates/_helpers.tpl:119`, `templates/scaledjob.yaml:51`, `templates/sandbox.yaml:8`); the `runtimeClassName` really is set on the SandboxTemplate pod (`templates/sandbox.yaml:11-13`). The DinD wording is correctly removed. - **AC4 — the #58 and #126 text is accurate.** `backlog.md:101` records the supersession with the right nuance (cold `SandboxClaim` per Run behind the unchanged API, warm pools excluded); `#126` is accurate: `SandboxWarmPool` renders at `replicas: 0` (`templates/sandbox.yaml:37`), the worker claims at boot (`pkg/worker/worker.go:132`), and the archived change names warm pools a non-goal (`openspec/changes/archive/2026-09-27-add-agent-sandbox-executor/proposal.md:42`). One thing to confirm with the owner: AC4 says warm pools "get their own ticket" but #126 is a repository backlog entry, not a tracker ticket, and `apps/ploeg/AGENTS.md` says priorities live on the tracker. If a Vikunja ticket was expected, that part is unmet — I could not verify the tracker from here. Non-blocking. - **AC5 — done.** `EXPERIMENTAL` is untouched (`apps/ploeg/ops/helm/ploeg/values.yaml:121`), so AC5's "do not remove it" holds. - **Checks.** The configured gate `PLOEG_VERIFY_SCRIPT` (`test -z "$(gofmt -l apps/ploeg)"`) passes. `python3 scripts/docs.py --check` passes every validator and repository-link check ("376 sources, 1984 repository links") and fails only at `mkdocs build` because `mkdocs` is not installed — exactly as the PR body reports under "Checks left to CI". That is honest and correct. - **Repository rules.** No `AGENTS.md`, `CLAUDE.md`, `.claude/`, `.agents/`, `.openhands/`, `.mcp.json` or `.cursorrules` changes. Conventional commit, `VIK-573` trailer, branched from `development`, PR base is `development`. Good. ### What I would change before merge Deliver AC1 and AC2 (the kind e2e workflow plus any manifests/values it needs), or have the owner re-scope VIK-573 first. Do not merge this as the completion of VIK-573: with the e2e moved out, the ticket's Outcome is not met, and the ticket was explicitly told to escalate rather than silently descope. <sub>Posted by Ploeg on behalf of the reviewing agent. It could not push to this branch.</sub>
docs(ploeg): record the kind runner blocker for the sandbox e2e
Some checks failed
[Workflow] On Pull Request / tutorial-smoke (pull_request) Successful in 26s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 35s
[Workflow] On Pull Request / checks (pull_request) Failing after 2m38s
8fa34402db
Backlog #127 gains its concrete blocker: the pull-request runner is a remote
Docker daemon with no shared filesystem, so kind can neither provision a node
nor load images, and a kind-capable runner is a homelab-cluster change. The CI
docs carry the same finding under "Cluster end-to-end tests" so the next person
does not land a job that can never pass.

VIK-573
Agent-Trace-Id: ploeg-ee113a5d0613
docs(ploeg): drop an anchor link that strict mkdocs rejects
All checks were successful
[Workflow] On Pull Request / tutorial-smoke (pull_request) Successful in 29s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 33s
[Workflow] On Pull Request / checks (pull_request) Successful in 4m40s
d85c0d084e
The #127 sentence linked #58 through a hand-written same-page fragment with
doubled hyphens. docs.py normalises repeated hyphens on both sides so it passed
that check, but mkdocs resolves the slug verbatim and the CI docs gate runs with
--strict, so the malformed anchor would warn and fail the build. Refer to #58 as
plain text instead; item 58 lives in section F, not the section the fragment
named.

VIK-573
Agent-Trace-Id: ploeg-ee113a5d0613
All checks were successful
[Workflow] On Pull Request / tutorial-smoke (pull_request) Successful in 29s
[Workflow] On Pull Request / release-policy (pull_request) Successful in 33s
[Workflow] On Pull Request / checks (pull_request) Successful in 4m40s
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin agent/vik-573:agent/vik-573
git switch agent/vik-573

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch development
git merge --no-ff agent/vik-573
git switch agent/vik-573
git rebase development
git switch development
git merge --ff-only agent/vik-573
git switch agent/vik-573
git rebase development
git switch development
git merge --no-ff agent/vik-573
git switch development
git merge --squash agent/vik-573
git switch development
git merge --ff-only agent/vik-573
git switch development
git merge agent/vik-573
git push origin development
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/glide!3
No description provided.