Skip to content

chore: bootstrap FuzeSDLC capability installer (wave 6) - #155

Open
izzywdev wants to merge 4 commits into
mainfrom
chore/sdlc-bootstrap-v1
Open

chore: bootstrap FuzeSDLC capability installer (wave 6)#155
izzywdev wants to merge 4 commits into
mainfrom
chore/sdlc-bootstrap-v1

Conversation

@izzywdev

Copy link
Copy Markdown
Owner

Summary

Adopts the FuzeSDLC canonical capability set (schema-validate, core-agents, workflows, community, roles, npm-registry, node-toolchain, entity-identity scaffolding, design-system conformance gate, skills, chart, argo-app, portal-registration) via scripts/bootstrap/main.py --adopt-canonical. This is Wave 6 (highest blast radius, sequenced last).

mf-remote was explicitly SKIPPED

FuzeAgent is one of four repos whose Module-Federation config shares React as a bare array (shared: ['react','react-dom']), which carries no singleton semantics, and currently works only because it bundles its own React copy. Converting that to a real MF singleton is exactly the change that produces a green build with a runtime-only "Invalid hook call" — nothing in CI catches it, and the required browser check has not been run. Every invocation used --skip mf-remote.

Verified React was left untouched:

  • services/ui-react/package.json: "react": "^18.3.1" (unchanged)
  • services/ui/package.json: "react": "^18.2.0" (unchanged)
  • services/ui-react/vite.config.ts: no diff at all — MF shared config untouched

Only engines.node/engines.npm and .nvmrc were raised per the toolchain floor (Node 24) — these are independent of the React/MF singleton question. node-toolchain itself reports that e2e/package.json and services/ui-react/package.json sit beside a committed lockfile, so no dependency floor was applied there either — engines/.nvmrc only.

Frozen A2A contract — untouched

agent-templates/contracts/a2a/v1/ (the frozen A2A contract other repos mirror) has zero diff against origin/main. Confirmed via git diff origin/main -- agent-templates/contracts/a2a/v1/ producing no output.

Auth — not touched

Per the tracked hand-rolled-HS256-with-shared-secret finding, auth code was left alone. Confirmed no diff/status changes to services/orchestrator/auth.py, agent-templates/a2a/authz.py, or any other *auth* file.

--adopt-canonical was destructive here — what it overwrote

21 UNTRACKED files (no provenance record, i.e. this repo had hand-edited or hand-authored copies that predated the installer) were overwritten with canonical content. Full list: 6 .claude/agents/*.md (contract-designer, backend-engineer, test-engineer, devops-engineer, agile-manager, security), 13 .github/workflows/*.yml, agent-templates/sync/role_loader.py, scripts/sync-chart-files.sh.

Inspected every workflow diff (git diff origin/main -- .github/workflows/) for self-hosted runners, custom triggers, extra steps, or a2a/mcp maintainer wiring that might be silently dropped:

  • No runs-on: self-hosted was removed anywhere (grep for removed runs-on/self-hosted lines returned nothing).
  • Real finding — claude-ci-autofix.yml trigger list changed to names that don't fully exist in this repo. The old (untracked) version watched workflow_run: workflows: ["CI/CD Pipeline", "FuzeAgent Test Suite"], which match this repo's actual workflow names (ci.yml → "CI/CD Pipeline", test.yml → "FuzeAgent Test Suite"). The canonical replaced this with ["Infrastructure Tests", "Helm Chart Validation", "Harden Gate"]. "Helm Chart Validation" and "Harden Gate" exist here; "Infrastructure Tests" does not — there is no workflow by that name in this repo. Net effect: the auto-fix workflow no longer triggers off this repo's actual CI (ci.yml) or test suite (test.yml) failures. Flagging rather than silently accepting — may need a follow-up to add this repo's real workflow names back into the trigger list, or to rename ci.yml/test.yml to match the canonical naming convention if that's the intended convergence.
  • auto-merge.yml: canonical drops a continue-on-error: true around the gh pr merge --auto step — this is a behavior change (a real merge-enable failure now surfaces instead of being swallowed), called out as intentional in the canonical's own comment, not a loss of local logic.
  • sync-chart-files.sh and role_loader.py: canonical versions are strict supersets of the local logic (chart-dir auto-discovery, additional registration-file sync, structured MCP-server-drop logging) — nothing repo-specific appears lost.
  • a2a-maintain.yml / mcp-maintain.yml: only action-pin hardening (actions/checkout@v4 → pinned SHA), no trigger/step changes, no contract touches.

Installer findings (verbatim)

! [node-toolchain] 2 package.json (e2e/package.json, services/ui-react/package.json) sit beside a committed lockfile, so the React/@types DEPENDENCY floor was NOT applied there — only engines and .nvmrc, which are not part of the locked tree. Raising a range out from under a lock makes `npm ci` refuse outright ("lock file's react@18.3.1 does not satisfy react@19.2.8"), and this installer cannot regenerate a lockfile: that needs a real `npm install` with network and registry auth, which is not something to run across 20 checkouts. The dependency floor for these belongs in its own PR that bumps package.json AND regenerates the lock in the same commit — which is the `chore/react-19` PR the rollout plan always called for as a separate step. Until then gate-toolchain will report these as below the floor, which is accurate.
! [entity-identity] .fuze/manifest.json declares no `identity.namespace`. It is not written automatically: the namespace becomes the visible prefix on every id this repo ever mints, and the gate's own rule is that it is declared rather than derived so a repo rename cannot orphan already-issued ids. Nothing is blocked until this repo declares an id registry, at which point gate-identifier will require it. Suggested when you do: "identity": { "namespace": "fuzeagent" }
! [entity-identity] no identity package is declared. Node repos install `@izzywdev/fuzefront-identity`; Python repos install `fuzefront-identity` from a tagged GitHub Release wheel (GitHub Packages has no PyPI registry). gate-identifier's --adoption check fires only once this repo has entity work, so this is a heads-up rather than a failure.
! [skills] 2 skill(s) referenced by this repo's agents do not exist in the canonical: frontend-design, mcp-builder. The agents will load nothing for them and carry on silently — an agent that cannot find its skill does not error, it improvises, and the difference is invisible in the output. Either add the skill to FuzeSDLC's skills/ or remove it from the agent's frontmatter.
! [portal-registration] deploy/helm/a2a-shared/files/ does not exist, so the registration payload is not inside the chart. Helm cannot read files outside a chart, so the init container has nothing to submit. Run scripts/sync-chart-files.sh.
! [mobile-pwa] no manifest.webmanifest. Not generated automatically: a webmanifest is only meaningful with real icons at real sizes and a service-worker scope matching how the app is served, none of which the canonical knows. A generated one pointing at icons that do not exist installs an app with a broken icon and passes every file-present check on the way.
! [secrets] workflows reference 9 repo secret(s): ANTHROPIC_API_KEY, CF_ACCESS_CLIENT_ID, CF_ACCESS_CLIENT_SECRET, FUZESDLC_AGENT_PUSH_TOKEN, FUZESDLC_DEPLOY_KEY, GHCR_PAT, GH_APPROVE_TOKEN, GOOGLE_API_KEY, OPENAI_API_KEY. This is a REPORT — whether each is actually set is deliberately not checked, because verifying it would require this process to hold credentials it should not have.
! [secrets] no deploy/sealed-secrets/ directory, while this repo has a Helm chart. Cluster secrets are delivered as SealedSecrets; without them the chart deploys with whatever defaults its values carry.

No canonical/baselineRef mismatch was hit, so --allow-canonical-drift was never needed.

Verification

  • --dry-run then --adopt-canonical then --adopt-canonical again → second adopt run reported DONE — 0 file(s) would be written; 0 not matching canonical (idempotent, confirmed).
  • 87 files written total, 21 of them UNTRACKED overwrites (reviewed above).

Rules followed

No gate/test/check was weakened. No continue-on-error, || true, deleted assertion, or allowlisted scanner finding was added by this PR (the one continue-on-error: true removal in auto-merge.yml came from the canonical itself, tightening rather than loosening).


Generated by Claude Code

Adopt the FuzeSDLC canonical capability set: schema-validate, core-agents,
workflows, community, roles, npm-registry, node-toolchain (engines/.nvmrc
only), entity-identity scaffolding, design-system conformance gate, skills,
portal-registration, and hardening pins across .github/workflows.

mf-remote is explicitly skipped: FuzeAgent shares React via a bare array
(no singleton semantics) and currently bundles its own React copy. Converting
that to a real MF singleton is exactly the change that produces a green build
with a runtime-only "Invalid hook call", so it is deferred to its own PR
with a real browser check. React stays at ^18 in services/ui-react and
services/ui; only engines.node/npm and .nvmrc were raised.

The frozen agent-templates/contracts/a2a/v1/ A2A contract is untouched, and
auth (services/orchestrator/auth.py, agent-templates/a2a/authz.py) was not
touched, per the tracked hand-rolled-HS256 finding.
@izzywdev izzywdev added the auto-merge label Aug 18, 2026 — with Claude
@github-actions
github-actions Bot enabled auto-merge (squash) August 18, 2026 13:31
@izzywdev izzywdev closed this Aug 18, 2026
auto-merge was automatically disabled August 18, 2026 13:31

Pull request was closed

@izzywdev izzywdev reopened this Aug 18, 2026

Copy link
Copy Markdown
Owner Author

gate-identifier (Harden Gate) is failing — real pre-existing violation, not fixed here

The newly-installed gate-identifier job (part of Harden Gate) fails with 84 violations, all the same rule: POST create-body schemas that don't set additionalProperties: false (governance/identifier-standard.md §1). These are pre-existing in this repo's API contract — the gate simply did not exist here before this PR installed it.

All 84 are the same 28 operations duplicated across the 3 mechanically-synced copies of the spec (contracts/openapi.yaml, services/orchestrator/contracts/openapi.yaml, deploy/helm/fuzeagent/files/openapi.yaml — kept in sync by scripts/sync-chart-files.sh). The 28 distinct operations, all in contracts/openapi.yaml:

POST /agents
POST /agents/{agent_id}/tasks
POST /agents/from-template
POST /agents/{agent_id}/conversations
POST /agents/{agent_id}/conversations/{conversation_id}/messages
POST /tasks/{task_id}/human-response
POST /agents/{agent_id}/register
POST /tasks/{task_id}/file-operations/{batch_id}/approve
POST /mcp/call-tool
POST /agents/{agent_id}/mcp-setup
POST /tasks/{task_id}/coordinate
POST /organizations/{organization_id}/providers/{provider}/credentials
POST /agents/{agent_id}/model-configuration
POST /agents/{agent_id}/tasks/cost-estimate
POST /knowledge/organizations/{organization_id}/add
POST /organizations/{organization_id}/goals
POST /goals/{goal_id}/milestones
POST /milestones/{milestone_id}/tasks
POST /goals/{goal_id}/conversations
POST /conversations/{conversation_id}/messages
POST /goals/{goal_id}/track-progress
POST /knowledge/organizations/{organization_id}/documents
POST /knowledge/organizations/{organization_id}/url
POST /knowledge/teams/{team_id}/documents
POST /knowledge/teams/{team_id}/url
POST /knowledge/agents/{agent_id}/documents
POST /knowledge/agents/{agent_id}/url
POST /agents/{agent_id}/container/create

Not fixed in this PR, deliberately. Per repo policy: "Never weaken a gate, test or check to make CI pass" and "Newly-installed gates may fail on real violations. Report file and line; do not soften. Escalate rather than guess." Editing 28 operations' request-body schemas across the canonical contract is a real API-contract change with BOPLA implications (OWASP API3:2023) — that's contract-owner territory, not something to wave through inside a mechanical bootstrap-adoption PR. Two legitimate paths per governance/identifier-standard.md: add x-client-assigned-id: allowed with a reason to each operation that is deliberately exempt, or fix the schemas to reject a client-supplied id. Either needs contract-designer's judgment on each of the 28 operations, not a bulk edit here.

Escalating: this PR cannot reach a fully green Harden Gate without that follow-up. All other Harden Gate sub-jobs (gate-manifest, gate-authz, gate-pagination, gate-lint, gate-localup, gate-version, gate-test, gate-secret-scan, gate-dependency-scan, gate-ds-conformance, gate-sast, gate-build, gate-toolchain) are green; gate-identifier is the sole failure.


Generated by Claude Code

claude and others added 2 commits August 18, 2026 16:22
Re-runs the SDLC bootstrap installer against FuzeSDLC main (post #79,
#105-#110) to pick up canonical fixes merged since this PR was cut:
harden-gate now triggers on ready_for_review, gate-sast/gate-authz get
setup-python, runs-on labels are preserved per file, nightly crons are
derived per repo, and check-registration regains nav.section
validation.

Also intended to unstick CI on this PR's head, which was showing zero
check runs due to a webhook-delivery gap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions
github-actions Bot enabled auto-merge (squash) August 18, 2026 16:22
Comment thread .npmrc
Comment on lines +1 to +35
# Scope -> registry mapping for the private @fuzefront/* packages.
#
# READ THIS BEFORE CHANGING A SCOPE LINE. Publishing RENAMES the packages:
# scripts/publish-packages.mjs rewrites @fuzefront/<x> to @izzywdev/fuzefront-<x>,
# because GitHub Packages requires the scope to match the owning ACCOUNT and there
# is no `fuzefront` org. So the name you install is NOT the name you import:
#
# published @izzywdev/fuzefront-design-system <- what npm fetches
# imported @fuzefront/design-system <- what the code says
#
# A plain `"@fuzefront/design-system": "^1.0.0"` therefore 404s. That is not
# hypothetical — it is exactly what fuzeplan and fuzefinance have. Depend on it
# through an npm: alias instead, which keeps every import statement unchanged:
#
# "dependencies": {
# "@fuzefront/design-system": "npm:@izzywdev/fuzefront-design-system@^1.0.0"
# }
#
# @izzywdev is the scope npm actually resolves against the registry; the others
# are mapped so that any package still carrying an old scope name resolves too
# rather than silently falling through to the public registry and 404ing.
@izzywdev:registry=https://npm.pkg.github.com
@fuzefront:registry=https://npm.pkg.github.com
@fuzeone:registry=https://npm.pkg.github.com

# Auth comes from the environment — NEVER commit a token. In CI, GITHUB_TOKEN is
# already present; locally, export a PAT with read:packages.
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true

# WHY THIS FILE ALSO LIVES AT THE REPO ROOT, not only next to the consuming
# package.json: npm resolves .npmrc by walking UP from the current directory. A
# repo whose only .npmrc sits in packages/<x>/ gives no mapping at all to anything
# run from the root — CI steps, tooling, a fresh clone — and every private scope
# 404s there. FuzeBI had precisely that shape.
Comment on lines +1 to +35
# Scope -> registry mapping for the private @fuzefront/* packages.
#
# READ THIS BEFORE CHANGING A SCOPE LINE. Publishing RENAMES the packages:
# scripts/publish-packages.mjs rewrites @fuzefront/<x> to @izzywdev/fuzefront-<x>,
# because GitHub Packages requires the scope to match the owning ACCOUNT and there
# is no `fuzefront` org. So the name you install is NOT the name you import:
#
# published @izzywdev/fuzefront-design-system <- what npm fetches
# imported @fuzefront/design-system <- what the code says
#
# A plain `"@fuzefront/design-system": "^1.0.0"` therefore 404s. That is not
# hypothetical — it is exactly what fuzeplan and fuzefinance have. Depend on it
# through an npm: alias instead, which keeps every import statement unchanged:
#
# "dependencies": {
# "@fuzefront/design-system": "npm:@izzywdev/fuzefront-design-system@^1.0.0"
# }
#
# @izzywdev is the scope npm actually resolves against the registry; the others
# are mapped so that any package still carrying an old scope name resolves too
# rather than silently falling through to the public registry and 404ing.
@izzywdev:registry=https://npm.pkg.github.com
@fuzefront:registry=https://npm.pkg.github.com
@fuzeone:registry=https://npm.pkg.github.com

# Auth comes from the environment — NEVER commit a token. In CI, GITHUB_TOKEN is
# already present; locally, export a PAT with read:packages.
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true

# WHY THIS FILE ALSO LIVES AT THE REPO ROOT, not only next to the consuming
# package.json: npm resolves .npmrc by walking UP from the current directory. A
# repo whose only .npmrc sits in packages/<x>/ gives no mapping at all to anything
# run from the root — CI steps, tooling, a fresh clone — and every private scope
# 404s there. FuzeBI had precisely that shape.
Comment thread e2e/.npmrc
Comment on lines +1 to +35
# Scope -> registry mapping for the private @fuzefront/* packages.
#
# READ THIS BEFORE CHANGING A SCOPE LINE. Publishing RENAMES the packages:
# scripts/publish-packages.mjs rewrites @fuzefront/<x> to @izzywdev/fuzefront-<x>,
# because GitHub Packages requires the scope to match the owning ACCOUNT and there
# is no `fuzefront` org. So the name you install is NOT the name you import:
#
# published @izzywdev/fuzefront-design-system <- what npm fetches
# imported @fuzefront/design-system <- what the code says
#
# A plain `"@fuzefront/design-system": "^1.0.0"` therefore 404s. That is not
# hypothetical — it is exactly what fuzeplan and fuzefinance have. Depend on it
# through an npm: alias instead, which keeps every import statement unchanged:
#
# "dependencies": {
# "@fuzefront/design-system": "npm:@izzywdev/fuzefront-design-system@^1.0.0"
# }
#
# @izzywdev is the scope npm actually resolves against the registry; the others
# are mapped so that any package still carrying an old scope name resolves too
# rather than silently falling through to the public registry and 404ing.
@izzywdev:registry=https://npm.pkg.github.com
@fuzefront:registry=https://npm.pkg.github.com
@fuzeone:registry=https://npm.pkg.github.com

# Auth comes from the environment — NEVER commit a token. In CI, GITHUB_TOKEN is
# already present; locally, export a PAT with read:packages.
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true

# WHY THIS FILE ALSO LIVES AT THE REPO ROOT, not only next to the consuming
# package.json: npm resolves .npmrc by walking UP from the current directory. A
# repo whose only .npmrc sits in packages/<x>/ gives no mapping at all to anything
# run from the root — CI steps, tooling, a fresh clone — and every private scope
# 404s there. FuzeBI had precisely that shape.
function shippedContent(cmPath, cmText, key) {
const lines = cmText.split('\n')
const start = lines.findIndex(l =>
new RegExp(`^ ${key.replace('.', '\\.')}:\\s*\\|`).test(l)
uniq = sorted(set(locs))
# recurring AND across >1 file = an extraction signal
if len(uniq) >= threshold and len({l.split(":")[0] for l in uniq}) >= 2:
fp = hashlib.sha1(norm.encode()).hexdigest()[:12]
Comment thread services/ui-react/.npmrc
Comment on lines +1 to +35
# Scope -> registry mapping for the private @fuzefront/* packages.
#
# READ THIS BEFORE CHANGING A SCOPE LINE. Publishing RENAMES the packages:
# scripts/publish-packages.mjs rewrites @fuzefront/<x> to @izzywdev/fuzefront-<x>,
# because GitHub Packages requires the scope to match the owning ACCOUNT and there
# is no `fuzefront` org. So the name you install is NOT the name you import:
#
# published @izzywdev/fuzefront-design-system <- what npm fetches
# imported @fuzefront/design-system <- what the code says
#
# A plain `"@fuzefront/design-system": "^1.0.0"` therefore 404s. That is not
# hypothetical — it is exactly what fuzeplan and fuzefinance have. Depend on it
# through an npm: alias instead, which keeps every import statement unchanged:
#
# "dependencies": {
# "@fuzefront/design-system": "npm:@izzywdev/fuzefront-design-system@^1.0.0"
# }
#
# @izzywdev is the scope npm actually resolves against the registry; the others
# are mapped so that any package still carrying an old scope name resolves too
# rather than silently falling through to the public registry and 404ing.
@izzywdev:registry=https://npm.pkg.github.com
@fuzefront:registry=https://npm.pkg.github.com
@fuzeone:registry=https://npm.pkg.github.com

# Auth comes from the environment — NEVER commit a token. In CI, GITHUB_TOKEN is
# already present; locally, export a PAT with read:packages.
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true

# WHY THIS FILE ALSO LIVES AT THE REPO ROOT, not only next to the consuming
# package.json: npm resolves .npmrc by walking UP from the current directory. A
# repo whose only .npmrc sits in packages/<x>/ gives no mapping at all to anything
# run from the root — CI steps, tooling, a fresh clone — and every private scope
# 404s there. FuzeBI had precisely that shape.
Comment thread services/ui/.npmrc
Comment on lines +1 to +35
# Scope -> registry mapping for the private @fuzefront/* packages.
#
# READ THIS BEFORE CHANGING A SCOPE LINE. Publishing RENAMES the packages:
# scripts/publish-packages.mjs rewrites @fuzefront/<x> to @izzywdev/fuzefront-<x>,
# because GitHub Packages requires the scope to match the owning ACCOUNT and there
# is no `fuzefront` org. So the name you install is NOT the name you import:
#
# published @izzywdev/fuzefront-design-system <- what npm fetches
# imported @fuzefront/design-system <- what the code says
#
# A plain `"@fuzefront/design-system": "^1.0.0"` therefore 404s. That is not
# hypothetical — it is exactly what fuzeplan and fuzefinance have. Depend on it
# through an npm: alias instead, which keeps every import statement unchanged:
#
# "dependencies": {
# "@fuzefront/design-system": "npm:@izzywdev/fuzefront-design-system@^1.0.0"
# }
#
# @izzywdev is the scope npm actually resolves against the registry; the others
# are mapped so that any package still carrying an old scope name resolves too
# rather than silently falling through to the public registry and 404ing.
@izzywdev:registry=https://npm.pkg.github.com
@fuzefront:registry=https://npm.pkg.github.com
@fuzeone:registry=https://npm.pkg.github.com

# Auth comes from the environment — NEVER commit a token. In CI, GITHUB_TOKEN is
# already present; locally, export a PAT with read:packages.
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
always-auth=true

# WHY THIS FILE ALSO LIVES AT THE REPO ROOT, not only next to the consuming
# package.json: npm resolves .npmrc by walking UP from the current directory. A
# repo whose only .npmrc sits in packages/<x>/ gives no mapping at all to anything
# run from the root — CI steps, tooling, a fresh clone — and every private scope
# 404s there. FuzeBI had precisely that shape.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants