Skip to content

fix(agent-gateway): inject KB_AUTO_MCP_DISABLED on headless launch + durable job state#14

Merged
jorisjonkers-dev-agents[bot] merged 7 commits into
mainfrom
fix/issue-11-headless-kb-env-durable-state
Jul 10, 2026
Merged

fix(agent-gateway): inject KB_AUTO_MCP_DISABLED on headless launch + durable job state#14
jorisjonkers-dev-agents[bot] merged 7 commits into
mainfrom
fix/issue-11-headless-kb-env-durable-state

Conversation

@jorisjonkers-dev-agents

Copy link
Copy Markdown
Contributor

Fixes #11

Summary

Three defects in the headless launch path, two fixed in this PR:

Defect 1 — KB_AUTO_MCP_DISABLED not injected on headless/council launch

HeadlessJobManager.DefaultProcessFactory now sets KB_AUTO_MCP_DISABLED=1 in the child process environment for every headless launch. Council and headless workers running on hosted runners can no longer accidentally fire auto-KB recall or capture hooks. A new enableKbHooks: Boolean = false field on HeadlessLaunchRequest and HeadlessRequest (DTO) allows individual runs to opt back in explicitly.

Defect 2 — Headless job state is in-memory only

Job metadata is now persisted as a JSON sidecar file (headless-<id>.json) in the gateway state dir on every status transition (RUNNING → COMPLETED/FAILED/CANCELLED). HeadlessJobManager implements InitializingBean; afterPropertiesSet reloads sidecar files at startup so callers can still read final status and output of jobs that completed before the current Pod started. Jobs still marked RUNNING in a sidecar (process died mid-run) are re-surfaced as FAILED.

The sidecar format is a minimal hand-rolled JSON writer/reader with no new dependencies. A dedicated HeadlessJobSidecarTest covers round-trip for all status variants plus the idempotent overwrite.

Defect 3 — RunnerPodChatGenerator bypasses AgentGatewayClient (out of scope)

RunnerPodChatGenerator and the alignment with AgentGatewayClient.startHeadlessJob() live in agents-api, not agent-runtime. That fix is tracked separately; this PR does not touch agents-api. The agentKind typing concern (untyped string → WorkspaceAgentKind enum) is already resolved in agents-api via ChatGenerationProperties.runnerPodAgentKind; the remaining concern is routing through the shared client, which is an agents-api concern.

Boundary note — agents-api #14

Defect 2 (durable state) pairs with agents-api #14 (reconciliation). This PR covers the gateway-side sidecar persistence as specified in this issue. Coordinating the reconciliation handshake between agents-api and the gateway is left to agents-api #14.

…durable job state

Defect 1: HeadlessJobManager.DefaultProcessFactory now sets
KB_AUTO_MCP_DISABLED=1 in the child process environment for every
headless launch. Council/headless workers on hosted runners no longer
fire auto-KB recall or capture hooks by accident. An explicit
enableKbHooks=true field on HeadlessLaunchRequest and HeadlessRequest
DTO lets individual runs opt back in.

Defect 2: Job state is persisted to a JSON sidecar file
(headless-<id>.json) in the gateway state dir on every status
transition. InitializingBean.afterPropertiesSet reloads sidecar files
at startup: completed/failed/cancelled jobs are restored as-is; any
job still marked RUNNING is re-surfaced as FAILED because the
underlying process cannot have survived the Pod restart.

Defect 3 (RunnerPodChatGenerator/AgentGatewayClient alignment) is
tracked separately as an agents-api change; this commit does not
touch agents-api.

Fixes #11
@jorisjonkers-dev-agents jorisjonkers-dev-agents Bot added the type: bug Something is broken or behaving incorrectly. label Jul 10, 2026
…ss fixes

Fix ktlint style violations in HeadlessJobManager (chain newline) and
HeadlessJobSidecar (multiline when branches, buildString newline) flagged
by the CI lint pass. Restructure HeadlessJobSidecar.readString to avoid
the ComplexMethod suppression by extracting a unescape helper.

Fix a timing race in HeadlessJobManagerTest: waitUntil(status==FAILED)
could see the status update before the SPAWN/FAILURE telemetry event was
recorded. Add a secondary waitUntil for the telemetry event before
asserting.
Close the Files.list directory stream with use{} to avoid leaking a
file descriptor on every startup reload. Persist the recovered FAILED
sidecar back to disk so a RUNNING->FAILED recovery converges instead
of being re-applied with a fresh completedAt on every restart. Widen
the sidecar filename pattern to accept uppercase hex. Replace the
DefaultProcessFactory KB test with assertions that actually launch
through the factory and verify the injected (or absent) variable in
the child process environment.
…decar write race

shutdownNow interrupts in-flight job threads whose interrupt path
persists a final sidecar state file. Without awaiting termination the
write can race external cleanup of the state directory (observed as a
JUnit TempDir deletion failure in CI). destroy now waits up to five
seconds for job threads to finish their final transition.
@jorisjonkers-dev-agents jorisjonkers-dev-agents Bot merged commit c65f9b3 into main Jul 10, 2026
11 checks passed
@jorisjonkers-dev-agents jorisjonkers-dev-agents Bot deleted the fix/issue-11-headless-kb-env-durable-state branch July 10, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: bug Something is broken or behaving incorrectly.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inject KB_AUTO_MCP_DISABLED on headless/council launch + headless durability + RunnerPodChatGenerator cleanup

1 participant