Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/actions/llm-endpoint/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,32 @@ runs:
openai) model="$FALLBACK_OPENAI_MODEL"; keyref='os.environ/OPENAI_API_KEY' ;;
gemini) model="$FALLBACK_GEMINI_MODEL"; keyref='os.environ/GEMINI_API_KEY' ;;
esac
# Remember the head vendor's route for the "*" catch-all below.
[ -z "${head_model:-}" ] && head_model="$model" && head_keyref="$keyref"
echo " - model_name: ${v}"
echo " litellm_params:"
echo " model: ${model}"
echo " api_key: \"${keyref}\""
done
# CATCH-ALL. The vendor-named entries above are only reachable by a
# caller that knows to ask for "anthropic". claude-code-action does
# NOT: it sends its own model name (e.g. claude-opus-4-8), which
# matches nothing here, so LiteLLM answers
# 400 {'error': 'completion: Invalid model name passed in
# model=claude-opus-4-8'}
# and the rung writes that string as its assistant text, reports NO
# conclusion, and still exits 0. Downstream that is indistinguishable
# from "the model declined to work", so fuze-code-review abstained and
# went red on every PR that took the runner-local path -- while the
# job's own message blamed the workflow-self-modification guard, which
# was not involved (observed on FuzeInfra#823, a one-file Helm change).
#
# "*" routes any unrecognized model name to the HEAD vendor, so the
# chain works without every caller having to pass --model.
echo " - model_name: \"*\""
echo " litellm_params:"
echo " model: ${head_model}"
echo " api_key: \"${head_keyref}\""
echo ""
echo "router_settings:"
echo " num_retries: 0"
Expand All @@ -301,6 +322,10 @@ runs:
rest_yaml="${rest// /\", \"}"
echo " fallbacks:"
echo " - ${head}: [\"${rest_yaml}\"]"
# The catch-all must roll over too, otherwise a caller that does not
# name a vendor (claude-code-action) gets a single-vendor chain and
# an exhausted head key fails the run instead of advancing.
echo " - \"*\": [\"${rest_yaml}\"]"
fi
echo ""
echo "general_settings:"
Expand Down
34 changes: 23 additions & 11 deletions deploy/helm/fuzefront/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -259,21 +259,33 @@ mcp:
#
# `a2a-repos-git` is NOT needed: FuzeFront is a PUBLIC repo (verified via the
# GitHub API), so the repo-sync init container clones anonymously.
# `ghcr-pull` is NOT needed either: ghcr.io/izzywdev/fuzeagent-a2a is a public
# `ghcr-pull` is NOT needed either: ghcr.io/izzywdev/fuze-a2a is a public
# GHCR package (verified — anonymous token exchange + manifest GET succeeded).
a2a:
enabled: true
image:
repository: ghcr.io/izzywdev/fuzeagent-a2a
# `tag` alone would be a mutable moving target (image reference grammar
# allows `tag@digest` together — kubelet/containerd resolve via the
# digest, so this is fully immutable regardless of what `latest` is
# repointed to later; the `tag` half is cosmetic/readable only). Digest
# verified anonymously pullable 2026-08-23 (what
# ghcr.io/izzywdev/fuzeagent-a2a:latest resolved to at verification time
# — `latest` itself is never dereferenced at deploy time, only used to
# locate this digest). Re-pin when FuzeAgent's CI publishes a new build.
tag: "latest@sha256:c233bb47409300e769c68f84c12077e3a74db132f287fa218929742fbe48ccf6"
# CORRECTED 2026-09-02: ghcr.io/izzywdev/fuzeagent-a2a (this pin's prior
# value) is a stale/abandoned image — last pushed 2026-08-24, still
# anonymously pullable but no longer built by anything. FuzeAgent's own
# release.yml ("Build & push a2a-shared" step) builds
# agent-templates/a2a/Dockerfile — the ONE shared A2A runtime — and
# publishes it ONLY as ghcr.io/izzywdev/fuze-a2a:{sha,latest}; that is
# also what FuzeAgent's own deploy/helm/a2a-shared/values-prod.yaml points
# at. (gate-a2a's own I1 default + the runtime standard doc still name
# `fuzeagent-a2a`; that is already-flagged gate/doc staleness, FuzeAgent
# PR #257 — an I1 finding against this file is that, not a new fork.)
repository: ghcr.io/izzywdev/fuze-a2a
# Plain CI-built SHA tag (not a `tag@digest` compound — that form is a
# valid Docker/OCI image-reference grammar, but gate-a2a's registry check
# GETs the literal `tag` string as the manifest reference and 404s on the
# compound form, which is what caused the prior pin to read as
# unpullable even though the underlying digest was fine). Matches the
# tag FuzeAgent's own release.yml currently has live in
# deploy/helm/a2a-shared/values-prod.yaml. Verified anonymously pullable
# 2026-09-02 (anonymous GHCR token + manifest GET → HTTP 200,
# digest sha256:26b84ab9c04844c685b0312b660e934232338f79f5b86001fc9e2520d4a3b9a6).
# Re-pin when FuzeAgent's CI publishes a new build.
tag: "624956d8983b"
pullPolicy: IfNotPresent
service:
type: ClusterIP
Expand Down
39 changes: 28 additions & 11 deletions deploy/helm/fuzefront/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,23 @@ mcp:
a2a:
enabled: false
image:
repository: ghcr.io/izzywdev/fuzeagent-a2a
# Immutable; never `latest` in prod (values-prod.yaml pins by digest,
# encoded as `tag@digest` — see that file's comment). Left empty here
# (local/dev default; server disabled) so `helm template` with the base
# values alone never renders an unpinned image reference.
tag: ""
# ghcr.io/izzywdev/fuzeagent-a2a is a stale/abandoned image (last pushed
# 2026-08-24; anonymously pullable but no longer built). The image
# FuzeAgent's own release.yml actively builds and publishes on every
# release, and what its own a2a-shared/values-prod.yaml points at, is
# ghcr.io/izzywdev/fuze-a2a (agent-templates/a2a/Dockerfile). Corrected
# here 2026-09-02 — gate-a2a's own I1 default + the runtime standard doc
# still name the old `fuzeagent-a2a` repository (already flagged,
# FuzeAgent PR #257); a resulting I1 finding against this file is that
# known staleness, not a new fork.
repository: ghcr.io/izzywdev/fuze-a2a
# `latest` is the permitted dev-shape tag (forbidden only in
# values-prod.yaml, which pins an exact CI-built tag) — matches
# FuzeAgent's own a2a-shared/values.yaml base shape. A blank tag here
# previously made gate-a2a report the image as "not fully declared" even
# though the server is disabled; `latest` keeps the declaration complete
# and verifiable while still describing the disabled dev default.
tag: "latest"
pullPolicy: IfNotPresent
service:
# MUST be ClusterIP — ingress is Cloudflare-tunnel-only, and `inClusterUrl`
Expand All @@ -402,11 +413,17 @@ a2a:
# Unset would default to the SHARED server's card URL and this pod would be
# unreachable through its own card despite passing every probe.
inClusterUrl: ""
auth:
oidcIssuerUrl: ""
oidcDiscoveryUrl: ""
audience: a2a
callerClaim: repo
# `auth` is left UNSET (not an empty-valued object) in this disabled base
# shape on purpose: the frozen interface requires `oidcIssuerUrl` whenever
# `auth` is present at all, so a present-but-empty block is a gate-a2a C3
# fatal finding on a file that was never meant to carry a real issuer.
# Mirrors FuzeAgent's own a2a-shared/values.yaml base shape. values-prod.yaml
# supplies the real `auth:` block for the enabled deployment.
# auth:
# oidcIssuerUrl: ""
# oidcDiscoveryUrl: ""
# audience: a2a
# callerClaim: repo
cardSigning:
keySecretRef: { name: "", key: "" }
keyId: a2a-v1
Expand Down
25 changes: 25 additions & 0 deletions docs/a2a/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# FuzeFront's A2A surface — current state and what's left

> **Status update 2026-09-02 (a2a-maintainer verification pass).** Section 2
> below ("Deployment — devops-engineer") is **done**, not still open:
> `deploy/helm/fuzefront/templates/a2a.yaml` + the `a2a:` block in
> `values.yaml`/`values-prod.yaml` exist, `helm lint`/`helm template` clean,
> and `gate-a2a` passes except for two already-flagged, known-stale findings
> (see below). Also corrected in this pass: every reference below to
> `ghcr.io/izzywdev/fuzeagent-a2a` names a now-**abandoned** image (last
> pushed 2026-08-24, still anonymously pullable but no longer built by
> anything); the chart now correctly points at `ghcr.io/izzywdev/fuze-a2a`,
> which is what `izzywdev/FuzeAgent`'s `release.yml` actually builds and
> publishes today, and what its own `a2a-shared/values-prod.yaml` uses. Left
> the historical narrative below as written (it was accurate when this
> document was authored) rather than rewriting it.
>
> **Open cross-repo question, not resolved here (out of this agent's scope —
> single-repo, no cross-repo architecture calls):** `izzywdev/FuzeAgent`'s
> `deploy/helm/a2a-shared/values-prod.yaml` also lists a `FuzeFront` tenant
> entry (currently behind that file's own "STILL DO NOT MERGE" GO-LIVE gate).
> If that shared-server rollout proceeds while this repo's own per-product
> pod stays `enabled: true`, the same tenant name would be served by two
> different pods — the exact ambiguity `a2a-runtime-standard.md` §2 exists to
> prevent. Whoever runs that GO-LIVE needs to either drop the `FuzeFront`
> tenant entry from `a2a-shared` or disable this repo's own pod first;
> flagged for `platform-governance`/`devops-engineer`, not decided here.

**There is no A2A server in this repo, and there should not be one.** The shared
A2A runtime (Claude-driven, one image, zero product logic — system prompt,
skills, and tool access all arrive as mounted/env configuration) already
Expand Down
41 changes: 41 additions & 0 deletions governance/a2a-policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$comment": "Ratchet policy for gate-a2a (scripts/gate_a2a.py). See governance/a2a-runtime-standard.md §4 (FuzeSDLC). IF THIS FILE IS ABSENT THE GATE RUNS IN `fail` MODE — absence is not permission. Only the named worklist below is softened, and only for the rules that name it; a repo not on a worklist gets `fail` even while the mode reads `warn`. Seeded (not managed) from FuzeSDLC governance/a2a-policy.json — governance-sync seeds this once per repo and does not overwrite it on later syncs, because the ratchet worklist and the sealedSecretDirs customization below are this repo's own record of debt and layout, not fleet-wide config.",
"version": 1,
"skills": {
"adoption": "warn",
"$comment": "Rule 4.1 (a NAMED skill that does not resolve to .claude/skills/<name>/SKILL.md) ALWAYS fails and is not governed by this key — a new violation can never be ratcheted past. This key governs rule 4.2 (a2a.enabled repo whose serving roles declare ZERO bundle skills) and 4.3 (missing root CLAUDE.md). FuzeFront's app-shell-platform role currently declares zero bundle skills: none of this repo's .claude/skills/* bundles document the role's actual product knowledge (Module-Federation host shell, app registration/discovery, health/heartbeat, shell surface) — they are generic SDLC-process skills, not app-shell-platform domain knowledge, so none was claimed here rather than force-fitting an unrelated bundle name. CLAUDE.md itself is present at the repo root (rule 4.3 satisfied). Flip to `fail` once ratchet.knownUnadopted is empty.",
"ratchet": {
"owner": "@izzywdev",
"$comment": "Repos with a2a.enabled=true and zero bundle skills declared on their serving roles, measured on their LIVE default branch 2026-08-23 (FuzeSDLC governance/a2a-policy.json, unmodified list). Claiming FuzeFront's entry means giving app-shell-platform a real skills[] list pointing at a real .claude/skills/<name>/SKILL.md bundle that documents its actual domain behaviour — product/domain authorship, owned by frontend-engineer/backend-engineer, not a2a-maintainer. Removing the last entry is the trigger to set adoption=fail.",
"knownUnadopted": [
"izzywdev/FuzeFront",
"izzywdev/fuzeagent",
"izzywdev/fuzebi",
"izzywdev/fuzehub",
"izzywdev/fuzeplan",
"izzywdev/fuzesales",
"izzywdev/fuzeservice",
"izzywdev/fuzex"
]
}
},
"image": {
"repository": "ghcr.io/izzywdev/fuzeagent-a2a",
"$comment": "Unmodified from the FuzeSDLC canonical on purpose — this is the fleet-wide default and gate_a2a.py's I1 check compares every repo's declared a2a.image.repository against it. It is KNOWN STALE (ghcr.io/izzywdev/fuzeagent-a2a was abandoned 2026-08-24; the image FuzeAgent's release.yml actually builds and publishes today is ghcr.io/izzywdev/fuze-a2a — already flagged, FuzeAgent PR #257). FuzeFront's own values.yaml/values-prod.yaml correctly declare fuze-a2a; the resulting I1 finding here is that known, already-reported gate/doc staleness, not a real fork — do not silently point this repo back at the stale name just to make I1 pass."
},
"creds": {
"sealedSecretDirs": ["deploy/contabo/sealed", "deploy/sealed-secrets", "helm/sealed-secrets", "deploy/helm/sealed-secrets"],
"$comment": "Added `deploy/contabo/sealed` ahead of the FuzeSDLC-canonical default list: that is FuzeFront's ACTUAL SealedSecrets directory (deploy/contabo/sealed/a2a-fuzefront-secrets.yaml carries a2a-card-signing/jws.key; deploy/contabo/sealed/fuzefront-secrets.yaml carries LITELLM_MASTER_KEY) — the canonical default (deploy/sealed-secrets) does not exist as a secrets tree in this repo (it holds unrelated Permit.io policy files) and gate-a2a reported both real secretRefs as unwired until this was added.",
"externallyProvisioned": {
"$comment": "secretRef {name} values provisioned OUTSIDE this repo's git tree. Each entry MUST name who provisions it — an unexplained entry is treated as missing. The gate NEVER reads a secret VALUE from anywhere; these names exist only so a legitimately-external secret is not reported as unwired."
}
},
"memory": {
"mode": "client-only",
"$comment": "The A2A pod is a Chroma CLIENT of the family's existing Chroma service, using a per-tenant collection. It never runs a Chroma server: chromadb carries PYSEC-2026-311 (unfixable pre-auth code injection in the SERVER's collections handler via trust_remote_code), and FuzeAgent is unaffected only because it never serves that endpoint. See governance/a2a-runtime-standard.md §7 (FuzeSDLC)."
},
"apiSurface": {
"mode": "mcp-only",
"$comment": "The A2A pod reaches FuzeFront's API through FuzeFront's own MCP gateway (packages/mcp-gateway, per .fuze/manifest.json mcp block) pointed at the product's FULL OpenAPI document. A raw-REST fallback is REFUSED, not deferred — see governance/a2a-runtime-standard.md §8 (FuzeSDLC)."
}
}