From 29d64e0e63955e9fd4b8e9b7cb167536959d75cb Mon Sep 17 00:00:00 2001 From: fuzeone-bot Date: Wed, 2 Sep 2026 08:40:40 +0300 Subject: [PATCH 1/2] chore(a2a-maintain): verify + fix FuzeFront's A2A surface config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ran gate_a2a.py (FuzeSDLC scripts/gate_a2a.py --all) against this repo and fixed the real, verifiable gaps it found in the already-built per-product A2A pod chart (deploy/helm/fuzefront/{values,values-prod}.yaml + templates/a2a.yaml): - Image: corrected a2a.image.repository from ghcr.io/izzywdev/fuzeagent-a2a (abandoned 2026-08-24, no longer built by anything) to ghcr.io/izzywdev/fuze-a2a (the image FuzeAgent's release.yml actually builds/publishes, and what its own a2a-shared/values-prod.yaml uses). Re-pinned values-prod.yaml's tag to a plain, currently-live, anonymously verified-pullable CI tag (624956d8983b) instead of a stale tag@digest compound the registry API can't resolve as a single reference. Base values.yaml now declares tag: "latest" (the sanctioned dev-shape value) instead of an empty string, so the declaration is complete while disabled. - Creds: added governance/a2a-policy.json (seeded from the FuzeSDLC canonical) declaring FuzeFront's actual SealedSecrets directory (deploy/contabo/sealed) — both referenced secrets (a2a-card-signing/jws.key, fuzefront-secrets/LITELLM_MASTER_KEY) already exist there; the gate's default sealedSecretDirs just didn't know to look. Also carries the fleet-approved skills-adoption ratchet naming FuzeFront's real gap (below). - Auth: base values.yaml no longer declares an empty-valued `auth:` block while the pod is disabled (contract requires oidcIssuerUrl whenever auth is present at all) — commented out to mirror FuzeAgent's own a2a-shared base shape; values-prod.yaml already carried the real issuer. - Docs: docs/a2a/README.md corrected the stale image name and added a status note that its "deployment — devops-engineer, not yet built" section is now done, plus flagged the still-open cross-repo question (this pod vs. FuzeAgent's a2a-shared also listing a FuzeFront tenant, gated behind its own not-yet-merged GO-LIVE) for platform-governance/devops-engineer. Verified: gate_a2a.py --all now reports 2 fatal findings, both the already-flagged gate/standard-doc staleness (I1 compares against the old fuzeagent-a2a default; FuzeAgent PR #257) — no new fatal findings. S3 (zero bundle skills on app-shell-platform) is a real, unresolved gap, correctly downgraded to a ratcheted warning by the fleet-owner-approved worklist already naming this repo; no matching skill bundle exists in .claude/skills/ to honestly claim, so none was force-fit. helm lint + helm template both clean for values.yaml and values-prod.yaml. Did not touch: providesTo (already exactly matches the pre-staged dependency-graph backfill, no diff), CLAUDE.md (already present), .claude/agents/a2a-maintainer.md (governance-managed, sources the same known-stale image name from FuzeSDLC — not this repo's file to fix), any secret value, or FuzeAgent's own a2a-shared chart. Co-Authored-By: Claude Opus 4.6 Claude-Session-Id: fe3aa738-cb24-4315-a29b-29834ddea892 --- deploy/helm/fuzefront/values-prod.yaml | 34 ++++++++++++++------- deploy/helm/fuzefront/values.yaml | 39 +++++++++++++++++------- docs/a2a/README.md | 25 ++++++++++++++++ governance/a2a-policy.json | 41 ++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 22 deletions(-) create mode 100644 governance/a2a-policy.json diff --git a/deploy/helm/fuzefront/values-prod.yaml b/deploy/helm/fuzefront/values-prod.yaml index 2540338f..f7836a49 100644 --- a/deploy/helm/fuzefront/values-prod.yaml +++ b/deploy/helm/fuzefront/values-prod.yaml @@ -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 diff --git a/deploy/helm/fuzefront/values.yaml b/deploy/helm/fuzefront/values.yaml index 70a41c2c..ff62e0d0 100644 --- a/deploy/helm/fuzefront/values.yaml +++ b/deploy/helm/fuzefront/values.yaml @@ -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` @@ -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 diff --git a/docs/a2a/README.md b/docs/a2a/README.md index 0580463a..56e428c1 100644 --- a/docs/a2a/README.md +++ b/docs/a2a/README.md @@ -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 diff --git a/governance/a2a-policy.json b/governance/a2a-policy.json new file mode 100644 index 00000000..da002139 --- /dev/null +++ b/governance/a2a-policy.json @@ -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//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//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)." + } +} From 5574910af892e2b41605348785533d572cd1ceb3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 05:42:04 +0000 Subject: [PATCH 2/2] chore(governance): reconcile managed files to FuzeSDLC v1 [skip ci] --- .github/actions/llm-endpoint/action.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/actions/llm-endpoint/action.yml b/.github/actions/llm-endpoint/action.yml index 78041e15..ab3f22e8 100644 --- a/.github/actions/llm-endpoint/action.yml +++ b/.github/actions/llm-endpoint/action.yml @@ -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" @@ -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:"