Repository navigation
Description
Parent: #1
The agents runner runs Mozart and Leonardo on the platform
Caller: overnightworks/personas#22, step K: the live run of the agent Mozart. Its code has landed (overnightworks/personas#30, PR overnightworks/personas#32). personas' head (then agents') asked on 09.11.2026 for this item, so that #22 can record it as a blocker. Leonardo joined on 10.11.2026 (overnightworks/personas#61, the operator's „bitte noch leonardo hinzufügen! (da vinci)“, comment 6756608585). The operator ordered on 12.11.2026 that the platform builds what his first Skat game with Mozart and Leonardo needs, in the order #466, then this item, then skat's release pin (comment 6811230358 on #466, relaying his „ja sag ihr bescheid!“); the open list of that game is overnightworks/personas#3, comment 6805833230, rows 1, 2 and 4.
What personas asks for (personas' README at v2026.11.11.1 owns the configuration keys)
- The image is personas' release
ghcr.io/overnightworks/personas:2026.11.11.1@sha256:62b38f4bce62185cb096badbb360e604a679d52d94ee02526d595b0dbbd441ef, tagv2026.11.11.1on commitb5a011b3567c, which the operator said yes to on 12.11.2026 (comment 6810246868). Theimagesjob decides its attestation at the pull. The images atghcr.io/overnightworks/agentsand personas'v2026.11.09.3are not used. PERSONAS_CONFIGpoints at a TOML file in a ConfigMap that holds:- the realm's token endpoint;
- an absolute
ready_file; - the apps that listen: hopin and skat, each with its public MCP URL through the edge, its scope, and the tools its turns may call with
max_tool_calls; skat's tools are the names of skat'stools/listat the release the skat pin takes; - the
[gateway]table (backend,api_groupand[gateway.gateway]withoriginandkey_file); - one
[[agents]]entry each for Mozart and Leonardo: client id, client secret file andpersona_file. It sets nomax_turnsand noturn_period_seconds: the operator ruled on 09.11.2026 that agents start with no turn cap until he turns one on (An agent hears a conversation and speaks in it: one voice loop for every app agents#803, comment 6739177891).
- The persona files ship in the image at
/app/characters/mozart.mdand/app/characters/leonardo.md, and the configuration names them there. No ConfigMap holds a persona text: the operator's yes of 10.11.2026 (personas owns the texts, they ship in its image, the platform holds no copy) overtakes this item's earlier sentence that the persona file is configuration (comment 6806186967). - Three secret files are mounted from the Secret
personas: each agent's client secret, a copy of the value the Secretkeycloakholds for config-cli, and the runner's LiteLLM key. - Both persona files name the model
text/sonnet, which the gateway must offer. - Readiness is
test -e <ready_file>. The runner opens no port, needs no init and exits 0 on SIGTERM; the Pod's normal grace period is enough. - The runner runs as the non-root uid 1001, writes only its
ready_file, opens no port and spawns no process. Its egress: the realm's token endpoint and the MCP URLs of hopin and skat through the edge, and the LiteLLM front.
Known on main 1146b59
- The Keycloak cutover (P10: hal's public services move onto the platform and hal stops #235) is through: the platform's Keycloak is the issuer
https://auth.hallucinai.de/realms/hallucinai. realm.yamlholdsagent-mozartwith hopin's scope; The realm lets the agent clients into skat #466 addsagent-leonardoand skat's scope, audience and/apps/skat/mcpfor both.- LiteLLM offers
text/sonnet(infrastructure/litellm/litellm-config.yaml). The front admits a fixed list of namespaces; a key per app comes fromkeys.yamlthroughlitellm:key <app>, which readslitellm_keyof the Secret<app>in namespace<app>. - Every request names its person as
user, and the agents library names the agent itself, so each agent's spend is also bound by the person budget ofkeys.yaml.
Decided at the pull
- The namespace, app and Secret are
personas, after the repository's name. - The NetworkPolicy admits nobody and sends only to cluster DNS, the front's port 4000 and TCP 443 outside the pod and service networks, as
agent-egressdoes: a NetworkPolicy cannot name a host, and the token endpoint and both MCP URLs are public names behind Cloudflare. - The
clusterjob applies the Deployment at 0 replicas, as it does for cloudflared and the acestep worker: in k3d the public MCP URLs resolve to the live apps, so a pod there would either never turn Ready or call the PC's apps with a dummy secret. - The runner keeps no memory (
[memory]left out), so it has no Postgres and no backup; a restart starts each history empty. - Streaming: Traefik passes a streamed answer through as it arrives; the proof is the first turn on the PC, read as a successful spend-log row of the key
personas, by counts only.
Gaps
- The egress rule admits every public host on 443, not only the three names; the runner sends only to the URLs of its configuration.
- No CI cell proves the policy's reach or a streamed answer through the front; the PC's first game does.
- A person of hopin cannot add the agents to a call until hopin's adders setting (The AI service's legal section names agents the operator runs in a call #277); the skat loop answers only once skat's release with
/mcpruns (skat's pin, after this item). Until then the loop of that app is suspended and logged once.
Open for the operator
- The money: the key row
personasinkeys.yaml(budget and parallel limit per month), and whether each agent stays under the person budget of 2 dollars a month, which bounds an agent like a person. Recommended: budget 10, parallel 2, the person budget unchanged for the first game. - PD-5 of
docs/personal-data.md, a ruled sentence, names the namespaces the front admits; it gainspersonas. Recommended: yes, as the AI path's new caller is what his order of 12.11.2026 asks for.
Deliberately not in it
- The realm's objects for skat and
agent-leonardo: The realm lets the agent clients into skat #466. - The memory's database (Mozart remembers: today first, then the evening, then for good personas#47) and voice (In a call that speaks, Mozart hears and answers aloud personas#41).
- A budget per agent apart from the person budget.
Risk
Material: a new caller of the AI path with its own key and money budget, a copy of two client secrets, and a new egress to the internet.
version = 1
now = "Pulled on 12.11.2026 on the operator's order of the first Skat game with Mozart and Leonardo, after #466; personas v2026.11.11.1 is released and the body is corrected to it."
next = "The lane builds apps/personas, its Namespace, Kustomization, front admission and key row and the cluster job's changes; the operator writes the Secret personas and Leonardo's client secret into platform-secrets; after landing, litellm:key personas and litellm:keys."
done_when = "The Deployment personas runs personas' release 2026.11.11.1 at a full app ref in the restricted Namespace personas, reads PERSONAS_CONFIG with Mozart and Leonardo and hopin and skat as listening apps, mounts its three secret files from the Secret personas, is Ready by its ready_file on the PC, opens no port, stops on SIGTERM within its grace, sends only to cluster DNS, the LiteLLM front and TCP 443 outside the pod and service networks, and its key row stands in keys.yaml with its key registered by litellm:key."
scope = [".github/workflows/ci.yml", "apps/personas", "clusters/pc/apps.yaml", "docs/personal-data.md", "infrastructure/litellm/front.yaml", "infrastructure/litellm/keys.yaml", "infrastructure/namespaces", "tests/flux/reconcile_the_candidate_tree.sh", "tests/flux/root_and_tunnel_carry_no_spec_patches.sh", "tests/litellm/keys_table_drives_budgets_and_the_app_key.sh", "tests/secrets/encrypt_secrets_to_a_throwaway_age_key.sh", "tests/workloads/apps_run_at_their_full_ref.sh"]
size = "M"
whole = "One app's first deployment: its manifests, its Namespace, its Kustomization, its key row and front admission and the cluster job's 0-replica patch and dummy Secret are one change, since a part alone either never turns Ready, fails the cluster job or admits a sender that runs nothing."
Activity
FlexOr2 commentedon Nov 9, 2026
overnightworks/agents#35 landed on 09.11.2026 (PR overnightworks/agents#36, 99ac636), as agents' head reports. The body's two precondition sentences now hold on agents' side:
Imageproves it as PID 1 withdocker stop.So the Deployment needs neither an init nor
shareProcessNamespace. #281 is still blocked by #235 and agents' release.FlexOr2 commentedon Nov 9, 2026
agents' first release exists. agents' head dispatched it on the operator's yes, and its release run is green (Gate, Image, Tag).
v2026.11.09.1, on commit99ac63633efa38d61e5578d3f42ba2f9357cd2b6.ghcr.io/overnightworks/agents:2026.11.09.1@sha256:c5f8862f52499773af85bc07695b2379ef08e8002352b0df2c4864c91d78dabe. The digest comes from the Image job's push step. agents' token cannot read packages, so the digest is not cross-checked against GHCR; theimagesjob's attestation check decides it at the pull.That clears the item's second precondition. It stays blocked by #235 alone.
FlexOr2 commentedon Nov 9, 2026
agents' release for this item exists. It carries agents#37, which makes both turn-cap fields optional: leave both out, since setting only one is refused at start.
v2026.11.09.2atcbfd8917e27f2a536f1173e958c2a86d3aafb3e6. Release run 42618131199 succeeded.ghcr.io/overnightworks/agents:2026.11.09.2@sha256:eae4a6843c98735e262785b4e9fe9a56e99aa87e8df86cfb25373905b2bf7f9e.cbfd8917e27f, the commit the tag points to. The signature was not verified locally, because the registry read needs a login. Theimagesjob'sgh attestation verifyon the lane's pull request is that proof.ghcr.io/overnightworks/agents@sha256:2b4b608f…, most likely the attestation's own manifest. That is not the image.The item still waits for #235: Keycloak has to be live on the platform first.
FlexOr2 commentedon Nov 9, 2026
personas' first release under its own name exists. It is the one this item takes. The operator said yes to this version, and its release run is green (Gate, Image, Tag).
v2026.11.09.3, on commit9860838a73fddd4b1a972b4ddd2dda20288d99ab.ghcr.io/overnightworks/personas:2026.11.09.3@sha256:30bb68fca439a4968cbce9df2354d955007ec9aec1ebbc7fa260b43886a6b855.What personas' head checked. GitHub's attestation API lists one attestation whose subject is this digest under
ghcr.io/overnightworks/personas, built from9860838a73fd, the commit the tag points to. The signature was not verified locally; theimagesjob's attestation check decides it at the pull.What this item's body must change. It still states the names from before the rename:
agents' releaseghcr.io/overnightworks/personas, at the ref above. The older images stay atghcr.io/overnightworks/agentsand are not to be usedAGENTS_CONFIGPERSONAS_CONFIG.AGENTS_CONFIGis no longer read, and the runner refuses to start without the new variableThe rest stands:
[[agents]]in the TOML keeps its name, the image's user keeps uid 1001, the Keycloak names stay (agent-mozart), andmax_turnsandturn_period_secondsstay unset.One change in behaviour. This release carries agent-runtime v0.28.0, so every request to the gateway now carries
"stream": trueand reads a streamed answer. The route to LiteLLM must let a streamed response through.Correction. The digest in #281 (comment) is wrong:
sha256:c5f8862f…is themoby/buildkitimage the Image job pulls, not the runner's image. personas' head read it from the wrong log line.git grepover platform's tree finds neither that digest nor the old image name, so nothing uses it. v2026.11.09.2's digest was checked against its attestation and is right, but this item takes.3.FlexOr2 commentedon Nov 10, 2026
A second agent, from personas' head on 10.11.2026 (overnightworks/personas#61, the operator's „bitte noch leonardo hinzufügen! (da vinci)“): the runner's configuration gets a second
[[agents]]entry for Leonardo (client idagent-leonardo, its secret file, its persona) as a row of this item; the realm's client is #394. Open for this item's pull, with personas' head: where a persona's text comes from. This body says "The persona file is configuration and goes in a ConfigMap"; personas' head proposes that personas owns the texts as files in its repository and the platform takes them from a release. The platform's head prefers the text inside personas' image, named by the configuration, so the character has one owner and the platform holds no copy to refresh; the fallback is a byte-for-byte copy held equal to the release by a check, as the legal app texts are.FlexOr2 commentedon Nov 10, 2026
Asked by hopin's head on 10.11.2026 (the operator to it, on Mozart in a HopIn call: „ja ich würde es testen wenn es geht!“): what on the platform stands between him and that test. Read on main f52d27e:
https://auth.hallucinai.de/realms/hallucinai, which is still hal's Keycloak. hal's realm on itsorigin/mainholds noagent-mozart; the client, the groups andagent_ownerexist only in the platform's Keycloak, which goes live with P10: hal's public services move onto the platform and hal stops #235. hal is the predecessor and gets no new caller, so Mozart gets a token hopin accepts only after the issuer's move. This item is blocked by P10: hal's public services move onto the platform and hal stops #235 for the same reason.HOPIN_AGENT_ADDERSinapps/hopin/hopin.yamlfollows The AI service's legal section names agents the operator runs in a call #277 by its body; it needs the owner's subject and the group path, both the operator's (keycloak:agent-ownersets the owner;/agent-addersis declared inrealm.yaml).So the order is #235's issuer move, then this item and the adders line; whether his own test may run before #277 is his to rule and changes nothing before step 1.
FlexOr2 commentedon Nov 11, 2026
From personas' head, 11.11.2026 (to apply to this body at its pull, after checking the source): the persona files ship in personas' image at
/app/characters/<name>.md(overnightworks/personas#95, built after personas#93). personas' head names the operator's yes of 10.11.2026 (personas owns the texts, they ship in its image, the platform holds no copy) as overtaking this body's sentence that the persona file is configuration in a ConfigMap: Mozart's entry setspersona_file = "/app/characters/mozart.md", no ConfigMap holds a persona, and the image is a personas release that holds the files (v2026.11.09.3 holds none; tag and digest follow). The files name the modeltext/sonnet; if the platform's gateway offers other names after #235, personas changes one line before its release. The skat rights are #466.FlexOr2 commentedon Nov 12, 2026
personas
v2026.11.11.1is released (operator's yes, 12.11.2026), from mainb5a011b3567c423ad4e035408ad1757f920e5426.ghcr.io/overnightworks/personas:2026.11.11.1@sha256:62b38f4bce62185cb096badbb360e604a679d52d94ee02526d595b0dbbd441ef; the attestation names this image and that commit.v2026.11.09.3: turns at a view place (a Skat table), one turn per revision; every turn is told his history across apps; Mozart's and Leonardo's agent files ship in the image.persona_file = "/app/characters/mozart.md"and/app/characters/leonardo.md, no ConfigMap for persona texts; the gateway must offertext/sonnet, which both files name.FlexOr2 commentedon Nov 12, 2026
Build plan for this item (fresh Opus planner, read-only, 12.11.2026). Since it was written: #466's lane carries config-cli.yaml and the CI group list, and the Secret keycloak holds client-agent-leonardo (overnightworks/platform-secrets#55 landed).
#281 plan: the personas runner with Mozart and Leonardo
Read on platform main 1146b59, personas
v2026.11.11.1(b5a011b), agents libraryv0.31.0, hopinv2026.11.11.1,skat main cc5f93a, the #466 lane branch at 5867e33, and platform-secrets' tree (key names only).
1. Files the lane creates or changes
Production (7 files, about 150 lines; above the 3-file guide because one app's first deployment does not split, see
whole):apps/personas/personas.yaml(new)personasin namespacepersonas: replicas 1,Recreate, labelapp.kubernetes.io/name: personas,automountServiceAccountToken: false,enableServiceLinks: false,imagePullSecrets: [ghcr-pull], podrunAsNonRoot: true+seccompProfile: RuntimeDefault(the image's numeric USER 1001, so no runAsUser, as hopin), one containerpersonasatghcr.io/overnightworks/personas:2026.11.11.1@sha256:62b38f4bce62185cb096badbb360e604a679d52d94ee02526d595b0dbbd441ef, noports, envPERSONAS_CONFIG=/etc/personas/personas.toml, readinessexec: [test, -e, /tmp/ready](no liveness: a restart empties both histories), containerallowPrivilegeEscalation: false,readOnlyRootFilesystem: true,capabilities.drop: [ALL]; volumesconfig(the generated ConfigMap, at/etc/personas, read-only),secrets(Secretpersonas, at/run/secrets, read-only; the volume must be namedsecrets, which the CI generator reads),tmp(emptyDir: {medium: Memory}at/tmp, forready_file). Plus the NetworkPolicypersonas(section 3). No Service, no Ingress.apps/personas/kustomization.yaml(new)namespace: personas,resources: [personas.yaml],configMapGenerator: [{name: personas, files: [personas.toml]}], so a changed configuration changes the ConfigMap's hashed name and rolls the pod (the runner reads it once, at start), asinfrastructure/litellm/does.apps/personas/personas.toml(new)clusters/pc/apps.yamlpersonas:dependsOn: [platform-secrets, admission], path./apps/personas,prune: true,wait: true,timeout: 10m0s, interval 10m, source flux-system. Header comment gains personas.infrastructure/namespaces/namespaces.yamlpersonaswithkustomize.toolkit.fluxcd.io/prune: disabledandpod-security.kubernetes.io/enforce: restricted.infrastructure/litellm/front.yamlvalues: [hopin, presentator, songmaker, personas, gatus]; its comment "the three apps" becomes four.infrastructure/litellm/keys.yamlpersonas: {budget: <operator>, parallel: <operator>}(recommended 10 and 2).Documentation (same change):
docs/personal-data.mdPD-5: the front admitshopin,personas,presentator,songmakerandgatus. PD-5 is aruled sentence: needs the operator's yes (section 6).
layout has no
apps/personas/line, and "Every app's Deployment ... is Available with one Ready pod" no longer holdsfor personas in
cluster(0 replicas there, as already for the acestep worker).Tests and CI (not counted as production):
.github/workflows/ci.ymlAPP_MANIFESTSgainsapps/personas/personas.yaml.tests/secrets/encrypt_secrets_to_a_throwaway_age_key.shpersonas) app_keys="litellm_key mozart_client_secret leonardo_client_secret" ;;, so the dummy Secret holds the three keys (the*)arm would give hopin's). The loop already makespersonas/ghcr-pull.tests/flux/reconcile_the_candidate_tree.shpersonasthat replaces the Deployment's/spec/replicaswith 0, with the reason (k3d resolves the public MCP URLs to the live apps).tests/flux/root_and_tunnel_carry_no_spec_patches.shclusters/pc/apps.yaml:personasjoins the list of patched layers.tests/workloads/apps_run_at_their_full_ref.sh/music-worker,/web -> 8001/TCP...). It becomes keyed by$namespace/$name; personas' expectation is ingress empty and egresskube-system/kube-dns -> 53/TCP,53/UDP;litellm/litellm-front -> 4000/TCP;0.0.0.0/0 except 10.42.0.0/16,10.43.0.0/16 -> 443/TCP, which needs anipBlockarm in thesourcesexpression. The template dry-run there already provesrestrictedadmits the Pod.What
clusterthen demands of the ordinary app pattern, all met by the shape above: the Namespace enforcesrestrictedand carries
prune: disabled; the layer depends onplatform-secretsandadmissionand applied the candidate;Recreate; a full app ref;ghcr-pullof type dockerconfigjson and the Secretpersonasin platform-secrets'inventory, created before the Deployment; every container at a digest (
deployments_run_the_digests_they_name.shskips a 0-replica one); the
imagesjob verifies tagv2026.11.11.1-> b5a011b, the attestation by digest and thesigner (personas' release calls quality's
image.ymlat f7723ba, v0.11.0); the repository is public, the packagepulls through
ghcr-pulllike every app's. "Available with one Ready pod" is proven only on the PC.2. The configuration and where each value comes from
https://auth.hallucinai.de/realms/hallucinai, live on the platform's Keycloak since #235) plus Keycloak's fixed token path; Traefik'sauthrouter passesPathPrefix(/realms/hallucinai/).ready_file/tmpis the memory-backedemptyDir. personas removes it at start and creates it at the first answered standing.agent-mozart(realm.yaml today),agent-leonardo(#466's branch).personas/personas, keysmozart_client_secret(equal toclient-agent-mozartofkeycloak/keycloak) andleonardo_client_secret(equal toclient-agent-leonardo, which #466 needs inkeycloak/keycloaktoo). A Secret cannot be mounted across namespaces, so the copy is unavoidable; a rotation writes both, named in the YAML comment. Mounted read-only at/run/secrets.http://litellm-front.litellm:4000, as hopin'sHOPIN_AGENT__GATEWAY__ORIGIN; HTTP, soca_fileis forbidden by agents'ChatGateway. The library calls/v1/chat/completions(streamed) and/v1/models, both routes offront-route.yml./run/secrets/litellm_key, keylitellm_keyof Secretpersonas/personas, whichlitellm:key personasregisters (it reads exactly that name).sk-prefix and at least 16 characters, aslitellm_keys.shrequires.backendlitellm, the onlyBackendKind.api_group/services/litellm(the persons' AI right), of which neither service account is a member. Recommended/agents: every agent token carries it, it needs no new realm right, and only the two clients whose secrets the pod holds can spend the runner's key. The alternative,/services/litellmfor both service accounts, is a realm change in #466.text/sonnet, which both persona files name; LiteLLM offers it today (litellm-config.yaml,anthropic/claude-sonnet-5-5,max_tokens16384), andlitellm:keygives the key every model LiteLLM lists. Nothing has to change.https://hopin.hallucinai.de/mcp, scopemcp-hopin, audiencehttps://hopin.hallucinai.de/mcp(realm). hopin's own-account tools at v2026.11.11.1 aresay,leave_call,accept_invitation(the runner's own) andjoin_call(refuses an own account). Recommendedtools = ["say"]: personas' agents never leave on their own. Readiness comes from this loop, since hopin's standing answers today.https://skat.hallucinai.de/mcp, scopemcp-skat, audiencehttps://skat.hallucinai.de/mcp(SKAT_PUBLIC_ORIGIN+/mcp, #466). skat's running release 2026.11.09.1 has no/mcp: the loop gets 404, is suspended withhttp_404and logged once, and resumes after skat's pin, without a restart. skat main cc5f93a names 28 tools (read_table, read_series_list, read_paused_tables, play_card, auction_bid, auction_hold, auction_pass, auction_play, give_up_game, throw_down_cards, agree_to_give_up, decline_give_up, continue_after_result, agree_to_end_series, refuse_to_end_series, start_new_series, take_skat, put_back_cards, declare_game, ramsch_take_skat, ramsch_put_back_cards, ramsch_push_skat, say_kontra, open_table, sit_down, stand_up, read_rule_sets, save_rule_set). personas holds a turn only when the app still declares every bound tool (bound_declarationsreturns None otherwise and the turn does not run), so the list must be a subset of the release'stools/list: take skat's head's list for the release, and the skat pin lane checks it.max_tool_callshistory_bytesstays at its default 32768.3. The NetworkPolicy
personaspodSelector: {app.kubernetes.io/name: personas},policyTypes: [Ingress, Egress], no ingress rule (nobody), egress:kube-systempodsk8s-app: kube-dns, UDP and TCP 53;litellm, podsapp.kubernetes.io/name: litellm-front, TCP 4000 (the front's own policy admits thenamespace
personas);ipBlock 0.0.0.0/0 except [10.42.0.0/16, 10.43.0.0/16], TCP 443: the edge, for the token endpoint and both MCPURLs, the form
agent-egressuses. The gap (any public host on 443) is named in the body.4. Secrets in platform-secrets, and who writes them
namespaces/personas/personas.yaml, Secretpersonasinpersonas, Opaquelitellm_key,mozart_client_secret,leonardo_client_secretkeycloak.yaml, and every key after a file's first needs the PC's age key, which only he reads.namespaces/keycloak/keycloak.yamlclient-agent-leonardoregistry/personas/kustomization.yaml+ a line- personasinregistry/kustomization.yamlLanding order: platform's #281 PR first (it creates the Namespace; the layer
personasthen waits, Not Ready, for itsSecret), then the platform-secrets PR, since a namespace missing on the PC stops the whole
platform-secretsKustomization. #466 and its
client-agent-leonardoland before both.The operator's block, ready to paste, in the platform-secrets worktree the head creates for this step on a branch (path
assumed
~/git/platform-secrets-worktrees/281-personas; the head fixes it if aco names another):It prints only file, key and
added. If #466 has already writtenclient-agent-leonardo, the operator instead copiesit, as Mozart's line does. Then the agent commits, opens and lands that pull request.
After both land, the agent (kubectl on the PC, no sudo, no value seen) runs:
mise run litellm:key personas, thenmise run litellm:keys --planandmise run litellm:keys;mise run keycloak:agent-owner agent-leonardo <operator's username>and the same foragent-mozart(a second run printsnothing changed), since skat and hopin refuse a tokenwithout
agent_owner.5. Proof
cluster(0 replicas: template admitted byrestricted, Secrets and pull Secret in the inventory before theDeployment, full ref, layer order, the policy as rendered),
images(attestation), quality's gate.pod's log holds no
ConfigError; after skat's pin, the skat loop'sresumed; the first game's first turn as asuccessful spend-log row of the key
personas(counts only) proves the streamed route through the front.6. Open questions that are the operator's
personas. Recommended budget 10, parallel 2 (two agents, oneturn each at a time).
person_budget: 2ofkeys.yamlcaps each agent at 2 dollars a month, like every person. Whether a game fits is not verified (Sonnet5.5's price per token is not read here; each turn sends up to 32 KB of history plus the table). Options: keep it
for the first game (recommended: it fails visible, a turn ends with
turn_endedand the agent waits), raise theperson budget for everyone, or a later item for a per-agent budget row (
litellm:admincannot help: it refusesservice accounts and is unlimited).
docs/personal-data.mdgainspersonas: recommended yes, covered in substance by his order of 12.11.2026,but the commit has to name his yes.
7. What a first game can leave out without corrupting data or opening a hole
[memory]: no Postgres, no backup, no migration; a restart only empties the in-memory histories.Not to be left out: the NetworkPolicy (the pod holds the runner's key and two client secrets),
restrictedwith aread-only root, and
ghcr-pull.8. Risks and cross-lane findings
$(env:AGENT_LEONARDO_CLIENT_SECRET)but changes neitherinfrastructure/keycloak/config-cli.yaml(no env fromclient-agent-leonardo) nor its scope; thekeycloakSecrethas no
client-agent-leonardokey today, andci.yml'sAGENT_GROUPSstill lists the groups without/apps/skat/mcp. If config-cli refuses an unset variable, both its CI and the PC's apply fail, and Leonardo cannotsign in, which blocks this item's Leonardo half.
a stream that ends, so this costs reconnects, not correctness (not measured).
skattool list that is not a subset of the release'stools/listsilently leaves the agents unable to act at atable (seated but silent); the skat pin lane must compare them.
the operator with Mozart and Leonardo meets that.
The operator's approval of the PD-5 change in docs/personal-data.md i…
FlexOr2 commentedon Nov 12, 2026
State for this item, 12.11.2026: #466 landed as 36ee5fc and config-cli applied it on the PC (Job Complete at its first attempt, public discovery 200): the realm holds agent-leonardo, mcp-skat and /apps/skat/mcp. PR #473 (draft, tip 6b60029) is built and reviewed; open on it: (1) CI's
imagesis red with MANIFEST_UNKNOWN for the personas package: the operator grants overnightworks/platform Read under the package's "Manage Actions access" (asked 12.11.2026), then the job is re-run; (2) the gate's finding that the commit touching PD-5 names no pending yes is settled at the landing, whose squash body names the operator's yes and this item; (3) his word on the budget row (10, parallel 2; each agent under the 2-dollar person budget) and PD-5; (4) his Secrets block forpersonas(litellm_key and both client secrets; given in chat) and a platform-secrets lane that takes it withregistry/personas; (5) after the landing:litellm:key personas,litellm:keys,keycloak:agent-owner agent-mozart|agent-leonardo <his username>. Follow-up outside the claim:tests/litellm/front_key_check_routing_and_provider_egress.sh:157says "three app namespaces", stale.FlexOr2 commentedon Nov 12, 2026
From personas' head, 12.11.2026, for the runner's configuration: the runner offers an agent only the tools its app entry names (
toolsin the TOML is a list of names; a name the app no longer declares stops that app's turns). skat's next release addssay_no_wordbesidesay_kontra(skat#336, on skat's main): without it in skat's tool list an agent at a table with Kontra is asked at every revision until its own first card. So skat's entry for Mozart and Leonardo lists skat's whole tools/list at the release the cluster runs,say_no_wordincluded once that release is deployed, and not before (a name skat does not yet declare would stop the turns).FlexOr2 commentedon Nov 12, 2026
From skat's head, as personas' head asked: skat's tool names for the runner's configuration, so every added or renamed tool is seen here. At skat main 6c062a2 (they ship with skat's next release; read from the tool-name constants in skat's
src/skat/web/mcp.py, not yet confirmed against a livetools/list; I confirm against the released image and post the tag here):accept_invitation,agree_to_end_series,agree_to_give_up,auction_bid,auction_hold,auction_pass,auction_play,continue_after_result,declare_game,decline_give_up,give_up_game,open_table,play_card,put_back_cards,ramsch_push_skat,ramsch_put_back_cards,ramsch_take_skat,read_paused_tables,read_rule_sets,read_series_list,read_table,refuse_to_end_series,save_rule_set,say_kontra,say_no_word,sit_down,stand_up,start_new_series,take_skat,throw_down_cards.Several names are new or were renamed since skat's last release v2026.11.10.1 (among them
say_no_wordfrom skat#336, the threeramsch_*tools from skat#325 andaccept_invitationfrom skat#313); I have not diffed the two releases' live lists, so take the whole list above rather than a delta. A name listed here that the deployed skat does not declare stops that app's turns in the runner, so the list changes together with the skat release, not before.FlexOr2 commentedon Nov 12, 2026
Operator, chat 12.11.2026, on the open points: package grant „1 done“; budget „es gibt kein runner budget? … das budget ist das litellm budget des agenten!“ (so the limit that counts is the person budget each agent falls under; the row
personasinkeys.yamlis the same app-key row every app has and is never the bound with two agents); PD-5 „3ja zum sastz“ (his yes topersonasjoining the namespaces the front admits indocs/personal-data.md); owner „4. felix“; Secrets block „und 5 done whatever“. Done since:keycloak:agent-owner agent-mozart felixandagent-leonardo felixboth ended with the owner set;imagesre-run green on bb8872e; the Secrets are overnightworks/platform-secrets#56.FlexOr2 commentedon Nov 12, 2026
Landed as 0e2f9bf (PR 473); Secrets as overnightworks/platform-secrets 52dc663 (PR 57);
litellm:key personasmade the key,litellm:keys --plan0 changes. The pod pulled its image throughghcr-pulland is Ready since 03:36Z. Its log:startedfor both agents on hopin and skat; skatsuspended … code=http_403for both, expected until skat's release with its MCP surface is pinned; hopin not suspended. Next: skat's release pin, which also addssay_no_wordto the runner's skat tools;accept_invitationstays out (personas' head: the runner accepts by the library). Not yet seen: a first hopin turn and its spend row.FlexOr2 commentedon Nov 12, 2026
From personas' head, 12.11.2026, for skat's tool list in personas.toml: bind the tools an agent plays with (and
say_kontra,say_no_wordfrom the release that declares them), but notstand_up: an agent stays at a table until a person sends it away, and with skat's next releasestand_upworks during a hand, so a boundstand_upwould let the model leave a running game.accept_invitationis not bound either; the runner calls it itself.