Version: 1.0 — June 28, 2026
Status: Design (approved for implementation planning)
Scope: A consolidation/hardening bundle after all three leaf-session archetypes shipped (A #12, C #13,
B #14). Three items: (1) register the built leaf-session backend track in the milestone registry;
(2) align server.ts's isMainModule idiom with the rest of the package; (3) apply the
cron-dispatch securityContext hardening to the leaf-worker ScaledJob and the Knative service.
Purely additive/non-behavioral — no change to runLeaf, the gate, the contract, the queue, or KEDA
scaling logic.
Builds on: Milestone Registry, Async Leaf Completion, Scheduled Leaf Dispatch (the cron-dispatch securityContext pattern this generalizes), Human-Gate.
Out of scope (deferred): reclaimer-churn tuning (no observed problem; the async dual-trigger
lagCount+pendingEntriesCount already prevents the deadlock it guarded — speculative, YAGNI).
One-line goal. Make the repo's roadmap honest about what shipped, remove a fragile entrypoint-detection idiom, and bring the two agent-running pods up to the same non-root, least-privilege baseline the cron-dispatch pod already has — all verified by the existing gate smoke.
Problem. The registry has Phase-1 (M1–M7, built) and Phase-2 (Z1–Z7, design-only) tables, but
the actually-shipped leaf-session backend — the MVP contract and the three archetypes — is not
recorded in any table. The roadmap is stale w.r.t. what merged.
Change. Add a new section "Leaf-Session Backend (BUILT)" between the Phase-1 and Phase-2 sections, with a table:
| Slice | Spec | PR |
|---|---|---|
| MVP leaf-session invocation contract | 2026-06-26-mvp-leaf-session-contract-design.md |
#10, #11 (gate-7 resume) |
Async leaf completion (KEDA ScaledJob + queue) |
2026-06-27-async-leaf-completion-design.md |
#12 |
| Scheduled leaf dispatch (cron trigger on-ramp) | 2026-06-28-scheduled-leaf-dispatch-design.md |
#13 |
| Human-gate (gate-while-idle, Archetype B) | 2026-06-28-human-gate-design.md |
#14 |
A short paragraph notes: these realize the Capability Charter
§5 MVP core + §8 promote-post-MVP (human-gate, cron trigger), sit outside the M/Z numbering
(they are the MVP/charter track, not the credential plane), and that all three archetypes from the
Pipeline Archetypes evidence base are now built.
No existing registry rows change. PR numbers are recorded as-merged.
Problem. server.ts:184-186 detects "run as entrypoint" with a fragile substring match:
process.argv[1] && process.argv[1].includes("knative-server"). cron-dispatch.ts:75 and
leaf-job.ts use the exact, canonical idiom: process.argv[1] === fileURLToPath(import.meta.url).
The substring match would misfire for any argv path containing "knative-server" (e.g. a test runner
or wrapper invoked from such a directory).
Change. Add import { fileURLToPath } from "node:url"; and replace the guard with:
const isMainModule = process.argv[1] === fileURLToPath(import.meta.url);Invariant to preserve: the server must NOT auto-start when imported (the server.test.ts and
route tests import startServer). With the exact idiom, process.argv[1] during a vitest run is the
vitest binary, not server.ts, so the guard is false and no listener starts — same effective
behavior as today, verified by the existing server suite staying green.
Problem. The cron-dispatch pod (leaf-cron.yaml) is hardened (non-root, no priv-esc, read-only
rootfs, dropped caps, seccomp). The leaf-worker ScaledJob and the Knative service — both of
which run the harness image — have no securityContext at all (only a /work mount). They run as
root with default capabilities.
Change. Apply the cron-dispatch baseline to both pods:
- Pod-level:
runAsNonRoot: true,runAsUser: 65532(the image has noUSER/runs as root, sorunAsNonRootalone would refuse to start),seccompProfile: { type: RuntimeDefault }. - Container-level:
allowPrivilegeEscalation: false,capabilities: { drop: ["ALL"] },readOnlyRootFilesystem: truewith a writable/tmpemptyDir(tsx/node need a temp dir), plus a writableemptyDirat the agent's home/config dir if the Pi session writes there (getAgentDir()/SettingsManager— determined during live verification).
Knative wrinkle (the service only). A Knative Service gates several securityContext fields
behind feature flags. The two PVC flags are already enabled in setup-kind.sh
(kubernetes.podspec-persistent-volume-claim / -write). Hardening the ksvc may additionally
require kubernetes.podspec-securitycontext (and possibly kubernetes.containerspec-add-capabilities
for the drop). setup-kind.sh gets an idempotent patch enabling whatever the service manifest
needs; the exact flag set is pinned against the live cluster in the plan. The ScaledJob is a plain
Kubernetes Job template — no feature flag needed there.
readOnlyRootFilesystem is decided empirically (Option 1). Attempt true on both pods with the
writable mounts above; if the live gate smoke shows the worker/service need un-redirectable root-fs
writes (e.g. the agent writes outside /tmp and the home mount), fall back to
readOnlyRootFilesystem: false on that pod only (keep true on cron-dispatch) and document the
reason inline. The non-fs hardening (non-root, runAsUser, seccomp, no-priv-esc, dropped caps) is
applied unconditionally regardless.
Risks this surfaces (all caught by the live smoke):
runAsUser: 65532must be able to write the/workPVC (the gate writesresult_ref/markers there).fsGroup: 65532makes the harness's own writes group-owned, butfsGroupdoes NOT make a result directory created by a different (root) writer group-writable. Operational contract (confirmed in live verification — EACCES writing the gate marker): because/workis the orchestrator's store (charter G3) and the harness now runs as uid 65532, the orchestrator must provision the per-run result directories writable by uid 65532 (e.g. world-writable, or created under a sharedfsGroup: 65532with group-write). The live smoke models this bychmod 0777on the run dir it provisions. Document this in the operator runbook for non-root deployments. (The agent itself runs cleanly under the hardened context — only the cross-writer/workdirectory ownership needs this coordination.)- The leaf-worker performs
kubectl execintosandbox-0; a non-root uid + dropped caps must still do that (it is an API call, not a privileged syscall — confirmed working in live verification).
- Unit:
@sh/harnessand@sh/knative-serversuites stay green (the only code change is theisMainModuleguard; the server suite proves no auto-start on import). - Live gate (the gate that matters): rebuild the image (it includes the
isMainModulechange) withdocker buildx build --loadand verify the image actually contains the change beforekind load(a prior session wasted cycles on a buildxdocker-containerdriver that never loaded the result into the local store); redeploy theScaledJob+ service + anysetup-kind.shflag patch; runGATE_LIVE_SMOKE=1 deploy/knative/leaf-gate-smoke.sh. All 6 claims must still pass with the hardened pods (this exercises the leaf-worker running the full agent under the new securityContext, writing/work, andkubectl execinto the sandbox). The controller runs this live gate, never a subagent. - If
readOnlyRootFilesystem: truebreaks a pod, record the fallback tofalse(that pod only) and re-run until the smoke is green.
- Reclaimer-churn tuning (no observed symptom; dual-trigger already prevents the guarded deadlock).
- Any change to
runLeaf, the gate state machine, the leaf-session contract, the queue, KEDA scaling metadata, orsubmit_verdict. - Hardening any pod other than the leaf-worker
ScaledJoband the Knative service (cron-dispatch and sandbox-0 are out — cron-dispatch is already hardened; sandbox-0 is a separate concern). - A new container image (reuses the harness image).
- Milestone Registry — the doc §1 updates.
- Scheduled Leaf Dispatch — the cron-dispatch securityContext + the
isMainModuleexact idiom this generalizes; the deferred-follow-up note this closes. - Human-Gate — the live gate smoke (
leaf-gate-smoke.sh) used as the verification harness. - Capability Charter — the MVP/promote-post-MVP framing the registry section records.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com