Skip to content

temporal-engine subchart (off by default), with the RBAC the engine needs - #198

Merged
DavidNic11 merged 3 commits into
mainfrom
feat/temporal-engine-chart
Aug 7, 2026
Merged

temporal-engine subchart (off by default), with the RBAC the engine needs#198
DavidNic11 merged 3 commits into
mainfrom
feat/temporal-engine-chart

Conversation

@DavidNic11

Copy link
Copy Markdown
Collaborator

Fourth PR in the sequence from #194 (ADR 0036), following #196 and #197. Adds the temporal-engine subchart.

enabled: false, so this changes no behaviour. It's the last PR in the sequence that doesn't.

Enabling the engine is deliberately two steps:

temporal-engine:
  enabled: true                    # 1. deploy worker + gateway
agent-orchestrator:
  config:
    agentEngine: temporal          # 2. route turns to it

Step 1 alone changes nothing, which is what makes the rollout reversible — the engine can be deployed, watched and rolled back before a single turn depends on it.

Takes a Temporal address rather than bundling a server, so enabling it adds no stateful component. (That was the strongest argument against this whole thing; it doesn't apply, since the platform already runs Temporal.)

The part worth actual scrutiny: RBAC

The chart arrived from the fork granting only toolruns create/get, which was true when it was written and isn't now. templates/rbac.yaml adds:

  • secrets create/get/update/patch. This grant is the mechanism that keeps credentials out of Temporal's event history: the authorization pre-flight writes resolved values into a Secret and returns only the object's name, so nothing a workflow can see holds credential material and the kubelet is the only reader. Without this grant the engine cannot resolve a per-user credential at all.
  • agentruns create/get, granted only when the NATS bridge is enabled — bridged pod agents launch the ordinary AgentRun an unmodified upstream agent expects.
  • integrationroutes get/list/watch, on a new gateway ServiceAccount. The gateway watches routes for deterministic dispatch and previously ran with no identity at all.

Each mirrors a grant agent-orchestrator already holds, so enabling the engine doesn't widen the release's blast radius — it gives an equivalent one to a second workload. Worth checking that claim rather than taking it.

Values reused rather than duplicated

Defaults point at the orchestrator's own OPENAI_API_KEY secret and the shared callback secret, so one key serves both engines. catalog.namespace defaults to the release namespace instead of a hardcoded one.

qdrant.collectionPrefix defaults to te-, and the reason is in the values file: both engines index tools/skills/agents with different payload schemas, so a shared collection would have each reading records it cannot decode. Prefixing keeps them side by side in one instance.

This adds the first CI job that renders charts/agent-controller at all

Nothing in CI templated this chart before, so a broken subchart template would ship silently — that's true of the other three subcharts today and remains so; this PR only covers the new one.

Rendered standalone rather than through the umbrella chart, because the parent pulls open-webui and nats from remote repos and templating it needs a helm dependency build plus a network round trip to validate a subchart that has neither.

Both shapes are checked, and the second one is the point: defaults must render the minimum (no catalog-sync without Qdrant, no agentruns grant without the bridge), and everything-on must render at all — an optional block that only breaks when enabled is the one nobody notices.

Fork residue dropped

docs/platform/ (a Bitovi-specific ArgoCD + ECR deployment kit) and the Makefile's ecr-push target. The Makefile now just runs the same four checks CI does.

What this PR does not do

It doesn't enable anything. The next step — flipping agentEngine to temporal — needs the e2e/ suite run under AGENT_ENGINE=temporal, and that hasn't happened. 581 unit tests passing is precisely the evidence ADR 0030 says isn't enough: "none of the defects above were visible to unit tests; every one required the assembled system to be running."

Locally: helm lint clean, both renders parse, engine module green (12 packages), orchestrator 581 tests + typecheck clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_018Uj1SJ41DJJ8woZM7fd3DQ

DavidNic11 and others added 3 commits August 2, 2026 21:31
…needs

The engine's standalone chart becomes a subchart of the umbrella chart, off by
default. Enabling it is deliberately TWO steps:

  1. temporal-engine.enabled=true              -- deploy worker + gateway
  2. agent-orchestrator.config.agentEngine=temporal   -- route turns to it

Step 1 alone changes no behaviour, which is what makes the rollout reversible:
the engine can be deployed, watched and rolled back before a single turn
depends on it.

Takes a Temporal ADDRESS rather than bundling a server — the platform already
runs a cluster (confirmed with the maintainer), so enabling this adds no
stateful component. That was the strongest argument against the whole change.

## RBAC: what the catch-up work actually needs

The imported chart only granted `toolruns` create/get, which was true when it
was written and is not now:

- `secrets` create/get/update/patch. This grant is what keeps a credential out
  of Temporal's event history: the pre-flight writes resolved values into a
  Secret and returns only its NAME, so nothing a workflow can see holds
  credential material and the kubelet is the only reader.
- `agentruns` create/get, granted ONLY when the NATS bridge is enabled —
  bridged pod agents launch the ordinary AgentRun an unmodified upstream agent
  expects.
- `integrationroutes` get/list/watch, on a NEW gateway ServiceAccount: the
  gateway watches routes for deterministic dispatch and previously ran without
  an identity at all.

Each mirrors a grant agent-orchestrator already holds, so enabling the engine
does not widen the release's blast radius — it gives an equivalent one to a
second workload.

## Values reused rather than duplicated

Defaults point at the orchestrator's own OPENAI_API_KEY secret and the shared
callback secret, so one key serves both engines. `catalog.namespace` defaults
to the release namespace instead of a hardcoded one.

`qdrant.collectionPrefix` defaults to `te-` and says why: both engines index
tools/skills/agents with DIFFERENT payload schemas, so a shared collection
would have each reading records it cannot decode.

## Verified, and now verifiable

Nothing in CI rendered charts/agent-controller at all, so a broken subchart
template would have shipped silently. A new job lints and renders this one in
both shapes that matter: defaults (no catalog-sync without Qdrant, no AgentRun
grant without the bridge) and everything-on, because an optional block that
only breaks when enabled is the one nobody notices. Rendered standalone, since
the umbrella chart pulls open-webui and nats from remote repos.

Locally: helm lint clean, both renders parse as YAML.

## Fork residue dropped

docs/platform (a Bitovi-specific ArgoCD/ECR deployment kit) and the Makefile's
`ecr-push` target are gone, and the Makefile now just runs the same four checks
CI does. The README's standalone-install section is replaced by the two-step
subchart flow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Uj1SJ41DJJ8woZM7fd3DQ
Working notes for landing ADR 0036 as five reviewable PRs rather than one
33-commit branch, with what each one is actually asking a reviewer to check.

Two things it records rather than leaves implicit. The ADR moved to PR 1 and out
of the dependency chain, so the design can be rejected before anyone reads 17k
lines of Go — if the answer is "we don't want a second agent loop", that is the
only PR that needed to exist. And PR 5 (flip the default) is deliberately
unwritten, because its precondition is an e2e run that has not happened; writing
it now would assert a result nobody has.

Also lists three findings that stand on their own merits whether or not the
engine is ever enabled, including a gap on the existing LangGraph path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Uj1SJ41DJJ8woZM7fd3DQ

@imaustink imaustink left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢 I triggered the jobs again

@DavidNic11
DavidNic11 merged commit 76860e4 into main Aug 7, 2026
6 of 12 checks passed
@DavidNic11
DavidNic11 deleted the feat/temporal-engine-chart branch August 7, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants