Skip to content

AI Prompt: Forma3D.Connect — Nx Cloud DTE Pipeline Optimization

Purpose: Instruct an AI to integrate Nx Cloud with Distributed Task Execution (DTE) into the Azure DevOps CI/CD pipeline, collapsing the sequential Validate → Test → Build stages into a single distributed stage for maximum parallelism
Estimated Effort: 8–12 hours
Prerequisites: Current Azure DevOps pipeline working (azure-pipelines.yml), Nx workspace functional
Output: Dramatically faster CI pipeline using Nx Cloud remote caching and Nx Agents for distributed task execution
Status: 🚧 TODO


🎯 Mission

Integrate Nx Cloud with Distributed Task Execution (DTE) into the Forma3D.Connect CI/CD pipeline. The current pipeline runs Validate (lint + typecheck), Test, and Build as three sequential stages — each with redundant dependency installations. DTE allows a single orchestrator job to distribute all Nx tasks (lint, typecheck, test, build) across multiple agent machines in parallel, dramatically reducing total pipeline duration.

Why This Matters:

The current pipeline has three critical bottlenecks:

  1. Sequential stages: Validate must finish before Test starts, Test must finish before Build starts
  2. Redundant setup: Node.js, pnpm, and pnpm install run separately in every job (Lint, TypeCheck, UnitTests, BuildAll) — that's 4× the install overhead
  3. Limited parallelism: Nx runs tasks with --parallel=3 on a single machine, but the workspace has 6+ projects — true distribution across machines would be faster

Phase delivers:

  • Nx Cloud workspace connection with remote caching
  • Distributed Task Execution using Nx Agents (or manual DTE with self-hosted Azure agents)
  • Collapsed Validate + Test + Build into a single distributed stage for feature branches
  • Preserved Docker packaging and deployment stages for main branch
  • Remote cache hits eliminating redundant work across branches

Critical constraints:

  • The Docker packaging jobs (PackageApi, PackageWeb, PackageDocs) and all deployment stages must remain unchanged — DTE only applies to Nx-managed tasks (lint, typecheck, test, build)
  • The pipeline must continue working if Nx Cloud is temporarily unavailable (graceful fallback)
  • Existing nx affected behavior must be preserved
  • Azure DevOps variable groups and secrets must not be exposed to Nx Cloud agents
  • The NX_CLOUD_ACCESS_TOKEN must be stored as an Azure DevOps secret variable

📌 Context (Current State)

Current Pipeline Structure

Validate ──→ Test ──→ Build & Package ──→ Deploy Staging ──→ Acceptance Test ──→ ...
  ├─ Lint         ├─ UnitTests    ├─ DetectAffected
  └─ TypeCheck                    ├─ BuildAll
                                  ├─ PackageApi
                                  ├─ PackageWeb
                                  └─ PackageDocs

Key observations:

  • Validate stage: 2 parallel jobs (Lint + TypeCheck), each runs install-dependencies.yml template separately
  • Test stage: 1 job (UnitTests) with PostgreSQL service container, runs install-dependencies.yml again
  • Build stage (main only): DetectAffected → then BuildAll + PackageApi + PackageWeb + PackageDocs in parallel
  • Nx parallelism: All Nx commands use --parallel=3 (single machine)
  • No Nx Cloud: nx.json has no nxCloudAccessToken or cloud configuration
  • No remote cache: Every branch rebuild recalculates everything from scratch

Current nx.json (Relevant Sections)

{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": ["default", "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", "..."]
  },
  "plugins": [
    { "plugin": "@nx/webpack/plugin" },
    { "plugin": "@nx/eslint/plugin" },
    { "plugin": "@nx/vite/plugin" },
    { "plugin": "@nx/jest/plugin" }
  ],
  "targetDefaults": {
    "@nx/js:tsc": {
      "cache": true,
      "dependsOn": ["^build"],
      "inputs": ["production", "^production"]
    }
  }
}

Current install-dependencies.yml Template

The template runs in every job and includes:

  1. Install Node.js 20.x
  2. Enable corepack + pnpm 9
  3. Cache pnpm store
  4. pnpm install --frozen-lockfile
  5. Fetch main branch for Nx affected (feature branches only)

Projects in Workspace

Project Targets
api build, serve, lint, test, typecheck
web build, serve, lint, test, typecheck
api-client lint, test
acceptance-tests lint, test (Playwright)
api-e2e lint, test
domain-contracts lint, test
ui lint, test, build
utils lint, test

🛠️ Tech Stack Reference

  • CI: Azure DevOps Pipelines (YAML), ubuntu-latest pool
  • Monorepo: Nx (no Nx Cloud yet)
  • Package manager: pnpm 9 via corepack
  • Node: 20.x
  • Database (tests): PostgreSQL 16 via service container
  • Docker: Multi-stage builds for API, Web, and Docs images

🏗️ Architecture: Two Implementation Options

Uses Nx Cloud's managed infrastructure to run agent machines. Simplest setup, no additional Azure agent configuration needed.

Pros:

  • Minimal CI config changes — one start-ci-run command
  • Nx Cloud optimally distributes tasks based on project graph and historical data
  • Remote caching across all branches and PRs
  • Built-in retry for flaky tasks
  • No need to manage agent pools

Cons:

  • Requires Nx Cloud subscription (free tier: 500 CI pipeline executions/month)
  • Tasks run on Nx Cloud infrastructure (data leaves Azure)
  • API tests needing PostgreSQL require special handling

Option B: Manual DTE with Self-Hosted Azure Agents

Uses Azure DevOps parallel jobs as DTE agents. Tasks are still coordinated by Nx Cloud, but run on your own infrastructure.

Pros:

  • Full control over agent environment (PostgreSQL available)
  • Data stays on Azure infrastructure
  • Can use existing Azure DevOps parallel job slots

Cons:

  • More complex pipeline configuration
  • Need to manage agent setup in each job
  • Requires at least 3–4 Azure parallel job slots

Recommendation: Start with Option B (Manual DTE) since the API tests require a PostgreSQL service container. Nx Cloud Hosted Agents can be adopted later once test infrastructure is decoupled.


📁 Files to Create/Modify

Modified Files

nx.json                                                    # Add nxCloudAccessToken
azure-pipelines.yml                                        # Restructure Validate + Test + Build stages
.azuredevops/pipelines/templates/install-dependencies.yml  # Minor updates for DTE compatibility
package.json                                               # Add nx-cloud dependency

New Files

.azuredevops/pipelines/templates/nx-agent.yml              # Reusable template for DTE agent jobs

🔧 Implementation Phases

Phase 1: Connect Workspace to Nx Cloud (1 hour)

Priority: Critical | Impact: High | Dependencies: None

1. Create Nx Cloud Account & Connect Workspace

Run in the workspace root:

pnpm nx connect

This will:

  • Create an Nx Cloud workspace
  • Add nxCloudAccessToken (or nxCloudId) to nx.json

2. Verify nx.json Configuration

After connecting, nx.json should include:

{
  "nxCloudAccessToken": "<your-read-write-token>"
}

Important: For CI, use a read-write token. The token added by nx connect is typically read-write. Store it as an Azure DevOps pipeline secret variable (NX_CLOUD_ACCESS_TOKEN) and remove the plaintext value from nx.json. Instead, the CI pipeline will pass it via environment variable.

3. Update nx.json for CI Token Injection

Replace the hardcoded token with a comment indicating it's injected via CI:

{
  "nxCloudAccessToken": "<token>"
}

Alternatively, keep the read-only token in nx.json for local developer caching and override with a read-write token in CI via the NX_CLOUD_ACCESS_TOKEN environment variable.

4. Add nx-cloud Dependency

pnpm add -D nx-cloud

5. Verify Remote Caching Works Locally

pnpm nx build api
# Run again — should show "remote cache hit"
pnpm nx build api

Phase 2: Add Azure DevOps Secret Variables (30 minutes)

Priority: Critical | Impact: High | Dependencies: Phase 1

1. Add NX_CLOUD_ACCESS_TOKEN to Pipeline Variables

Add to the forma3d-staging variable group (or create a new nx-cloud variable group):

  • Variable name: NX_CLOUD_ACCESS_TOKEN
  • Value: The read-write access token from Nx Cloud dashboard
  • Secret: Yes (mark as secret)

2. Update Pipeline Variables Section

Add to azure-pipelines.yml variables:

variables:
  - group: forma3d-staging
  # Nx Cloud
  - name: NX_CLOUD_ACCESS_TOKEN
    value: $(NX_CLOUD_ACCESS_TOKEN) # From variable group

Or pass via environment variable on each Nx command step.


Phase 3: Create DTE Agent Template (1 hour)

Priority: High | Impact: High | Dependencies: Phase 2

1. Create .azuredevops/pipelines/templates/nx-agent.yml

# ============================================================================
# Template: Nx Cloud DTE Agent
# ============================================================================
# Reusable template for Nx Cloud Distributed Task Execution agent jobs.
# Each agent connects to Nx Cloud and waits for task assignments.
# ============================================================================

steps:
  - checkout: self
    fetchDepth: 0
    fetchFilter: tree:0
    persistCredentials: true

  - task: NodeTool@0
    displayName: 'Install Node.js'
    inputs:
      versionSpec: '$(nodeVersion)'

  - script: |
      corepack enable
      corepack prepare pnpm@$(pnpmVersion) --activate
    displayName: 'Install pnpm'

  - task: Cache@2
    displayName: 'Cache pnpm store'
    inputs:
      key: 'pnpm | "$(Agent.OS)" | pnpm-lock.yaml'
      restoreKeys: |
        pnpm | "$(Agent.OS)"
      path: $(Pipeline.Workspace)/.pnpm-store

  - script: |
      pnpm config set store-dir $(Pipeline.Workspace)/.pnpm-store
      pnpm install --frozen-lockfile
    displayName: 'Install Dependencies'

  - script: pnpm prisma generate
    displayName: 'Generate Prisma Client'

  - script: npx nx-cloud start-agent
    displayName: 'Start Nx Cloud Agent'
    env:
      NX_CLOUD_ACCESS_TOKEN: $(NX_CLOUD_ACCESS_TOKEN)

Phase 4: Restructure Feature Branch Pipeline (3 hours)

Priority: High | Impact: Very High | Dependencies: Phase 3

This is the core change. For feature branches (non-main), collapse Validate + Test into a single stage with DTE.

Current Flow (Feature Branches)

Validate Stage           Test Stage
├─ Lint (affected)       └─ UnitTests (affected)
└─ TypeCheck
     ↓ sequential ↓           ↓ sequential ↓

New Flow (Feature Branches with DTE)

CI Stage (single stage)
├─ Main Job (orchestrator)     ← issues nx affected -t lint,typecheck,test
├─ Agent 1                     ← receives tasks from Nx Cloud
├─ Agent 2                     ← receives tasks from Nx Cloud
└─ Agent 3                     ← receives tasks from Nx Cloud

Updated azure-pipelines.yml — Feature Branch Stages

Replace the separate Validate and Test stages with a single CI stage for feature branches:

stages:
  # --------------------------------------------------------------------------
  # Stage: CI (Feature Branches) — Distributed with Nx Cloud
  # --------------------------------------------------------------------------
  # Uses Nx Cloud DTE to distribute lint, typecheck, and test tasks
  # across multiple agents for maximum parallelism.
  # Only runs on non-main branches.
  # --------------------------------------------------------------------------
  - stage: CI
    displayName: 'CI (Distributed)'
    condition: and(
      eq('${{ parameters.loadTestOnly }}', 'false'),
      ne(variables.isMain, true)
    )
    jobs:
      # DTE Agent jobs — these wait for task assignments from Nx Cloud
      - job: Agent
        displayName: 'Nx Cloud Agent'
        strategy:
          parallel: 3  # Number of agents — adjust based on workload
        pool:
          vmImage: 'ubuntu-latest'
        services:
          postgres:
            image: postgres:16
            ports:
              - 5432:5432
            env:
              POSTGRES_USER: postgres
              POSTGRES_PASSWORD: postgres
              POSTGRES_DB: forma3d_connect_test
        steps:
          - template: .azuredevops/pipelines/templates/nx-agent.yml
        env:
          DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'

      # Main orchestrator job — issues Nx commands, Nx Cloud distributes them
      - job: Main
        displayName: 'Nx Cloud Orchestrator'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: self
            fetchDepth: 0
            fetchFilter: tree:0
            persistCredentials: true

          - template: .azuredevops/pipelines/templates/install-dependencies.yml

          - script: pnpm prisma generate
            displayName: 'Generate Prisma Client'

          # Start DTE: distribute tasks to manual agents, stop after test target
          - script: npx nx-cloud start-ci-run --distribute-on="manual" --stop-agents-after="test"
            displayName: 'Start Nx Cloud DTE'
            env:
              NX_CLOUD_ACCESS_TOKEN: $(NX_CLOUD_ACCESS_TOKEN)

          # Run all targets in one command — Nx Cloud distributes across agents
          - script: pnpm nx affected -t lint,typecheck,test --parallel=2 --exclude=api-e2e,acceptance-tests
            displayName: 'Run Affected Tasks (Distributed)'
            env:
              CI: 'true'
              NX_CLOUD_ACCESS_TOKEN: $(NX_CLOUD_ACCESS_TOKEN)
              DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'

          - task: PublishTestResults@2
            displayName: 'Publish Test Results'
            condition: succeededOrFailed()
            inputs:
              testResultsFormat: 'JUnit'
              testResultsFiles: '**/junit.xml'
              mergeTestResults: true
              failTaskOnMissingResultsFile: false

Keep Main Branch Pipeline Intact

For the main branch, keep the existing Validate → Test → Build flow but add Nx Cloud remote caching for speed. The main branch needs Docker packaging which doesn't benefit from DTE.

Update the existing Validate and Test stages to include:

# Add to each Nx command step:
env:
  NX_CLOUD_ACCESS_TOKEN: $(NX_CLOUD_ACCESS_TOKEN)

This gives main branch runs free remote caching — if a feature branch already validated the same code, main will get cache hits.


Phase 5: Main Branch Build Stage with DTE (2 hours)

Priority: Medium | Impact: High | Dependencies: Phase 4

Optionally, also distribute the main branch Validate + Test stages. The Build stage's BuildAll job can also benefit from DTE, but the Docker packaging jobs (PackageApi, PackageWeb, PackageDocs) must remain as separate Azure jobs since they use Docker buildx directly.

Updated Main Branch Flow

  # --------------------------------------------------------------------------
  # Stage: Validate & Test (Main Branch) — Distributed with Nx Cloud
  # --------------------------------------------------------------------------
  - stage: ValidateAndTest
    displayName: 'Validate & Test'
    condition: and(
      eq('${{ parameters.loadTestOnly }}', 'false'),
      eq(variables.isMain, true)
    )
    jobs:
      - job: Agent
        displayName: 'Nx Cloud Agent'
        strategy:
          parallel: 3
        pool:
          vmImage: 'ubuntu-latest'
        services:
          postgres:
            image: postgres:16
            ports:
              - 5432:5432
            env:
              POSTGRES_USER: postgres
              POSTGRES_PASSWORD: postgres
              POSTGRES_DB: forma3d_connect_test
        steps:
          - template: .azuredevops/pipelines/templates/nx-agent.yml
        env:
          DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'

      - job: Main
        displayName: 'Nx Cloud Orchestrator'
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - checkout: self
            fetchDepth: 0
            fetchFilter: tree:0
            persistCredentials: true

          - template: .azuredevops/pipelines/templates/install-dependencies.yml

          - script: pnpm prisma generate
            displayName: 'Generate Prisma Client'

          - script: npx nx-cloud start-ci-run --distribute-on="manual" --stop-agents-after="build"
            displayName: 'Start Nx Cloud DTE'
            env:
              NX_CLOUD_ACCESS_TOKEN: $(NX_CLOUD_ACCESS_TOKEN)

          # Run ALL targets including build — Nx Cloud distributes across agents
          - script: pnpm nx run-many -t lint,typecheck,test,build --all --parallel=2 --exclude=api-e2e,acceptance-tests
            displayName: 'Run All Tasks (Distributed)'
            env:
              CI: 'true'
              NX_CLOUD_ACCESS_TOKEN: $(NX_CLOUD_ACCESS_TOKEN)
              DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'

          - task: PublishTestResults@2
            displayName: 'Publish Test Results'
            condition: succeededOrFailed()
            inputs:
              testResultsFormat: 'JUnit'
              testResultsFiles: '**/junit.xml'
              mergeTestResults: true
              failTaskOnMissingResultsFile: false

  # Build & Package stage now only handles Docker packaging
  - stage: Build
    displayName: 'Package Docker Images'
    dependsOn: ValidateAndTest
    condition: and(succeeded(), eq(variables.isMain, true), eq('${{ parameters.loadTestOnly }}', 'false'))
    jobs:
      - job: DetectAffected
        # ... (keep existing DetectAffected job unchanged)
      - job: PackageApi
        # ... (keep existing PackageApi job unchanged)
      - job: PackageWeb
        # ... (keep existing PackageWeb job unchanged)
      - job: PackageDocs
        # ... (keep existing PackageDocs job unchanged)

Phase 6: Graceful Fallback Without Nx Cloud (1 hour)

Priority: Medium | Impact: Medium | Dependencies: Phase 4

If Nx Cloud is unavailable, the pipeline should not fail. Add a fallback mechanism.

1. Use --fallback-on-error Flag

The start-ci-run command supports a fallback mode:

- script: npx nx-cloud start-ci-run --distribute-on="manual" --stop-agents-after="test" || echo "Nx Cloud unavailable, running locally"
  displayName: 'Start Nx Cloud DTE (with fallback)'

2. Environment Variable Fallback

Add a pipeline parameter for disabling DTE:

parameters:
  - name: disableDTE
    displayName: 'Disable Distributed Task Execution'
    type: boolean
    default: false

Use a condition to skip DTE when disabled:

- script: npx nx-cloud start-ci-run --distribute-on="manual" --stop-agents-after="test"
  displayName: 'Start Nx Cloud DTE'
  condition: eq('${{ parameters.disableDTE }}', 'false')

Phase 7: Verify and Tune (2 hours)

Priority: High | Impact: High | Dependencies: All previous phases

1. Verify Remote Caching

Run a feature branch pipeline twice — the second run should show cache hits for unchanged projects:

> NX   Successfully ran target lint for 6 projects (cache hit for 6)
> NX   Successfully ran target test for 5 projects (cache hit for 5)

2. Verify DTE Distribution

Check the Nx Cloud dashboard to confirm tasks are distributed across agents:

  • Navigate to your Nx Cloud dashboard
  • Find the CI run
  • Verify tasks are spread across Agent 1, Agent 2, Agent 3
  • Check for idle time — agents should be kept busy

3. Tune Agent Count

Start with 3 agents and adjust based on:

Workspace size Recommended agents
< 5 projects 2 agents
5–15 projects 3 agents
15–30 projects 4–5 agents
30+ projects 5–8 agents

Since the workspace currently has ~8 projects, 3 agents is a good starting point.

4. Monitor Azure DevOps Parallel Job Consumption

Each DTE agent uses one Azure DevOps parallel job slot. With 3 agents + 1 orchestrator = 4 parallel job slots needed. Verify your Azure DevOps organization has enough slots.


📊 Expected Performance Improvements

Feature Branch (lint + typecheck + test)

Metric Before (Sequential) After (DTE, 3 agents)
Install deps overhead 3× (Lint + TypeCheck + UnitTests) 4× but in parallel
Lint ~2 min ~40s (distributed)
TypeCheck ~2 min ~40s (distributed)
Test ~3 min ~1 min (distributed)
Total wall time ~9–12 min ~3–5 min
Speedup ~2.5–3×

Main Branch (lint + typecheck + test + build + package)

Metric Before (Sequential) After (DTE + Docker parallel)
Validate + Test + Build ~15–20 min ~5–8 min (DTE)
Docker packaging ~5 min (parallel) ~5 min (unchanged)
Total before deploy ~20–25 min ~10–13 min
Speedup ~2×

Cross-Branch Caching Bonus

When a feature branch is merged to main, the main branch pipeline will get remote cache hits for all tasks that were already validated on the feature branch. This can further reduce main branch pipeline time to near-zero for the validate/test/build steps.


✅ Validation Checklist

Setup

  • Nx Cloud workspace created and connected
  • nx.json updated with cloud configuration
  • nx-cloud package added to devDependencies
  • NX_CLOUD_ACCESS_TOKEN stored as Azure DevOps secret variable
  • Remote caching verified locally (nx build api shows cache hits on second run)

Pipeline — Feature Branches

  • Single CI stage replaces Validate + Test stages
  • 3 DTE agent jobs start and connect to Nx Cloud
  • Orchestrator job runs nx affected -t lint,typecheck,test
  • Tasks are distributed across agents (verify in Nx Cloud dashboard)
  • PostgreSQL service container available on agent jobs for API tests
  • Test results still published to Azure DevOps
  • Pipeline completes faster than before

Pipeline — Main Branch

  • ValidateAndTest stage uses DTE for lint, typecheck, test, build
  • Docker packaging stage (PackageApi, PackageWeb, PackageDocs) unchanged
  • DetectAffected still outputs correct variables for deployment stages
  • Deployment stages (DeployStaging, AcceptanceTest, etc.) unaffected
  • Remote cache hits visible when feature branch already validated the code

Fallback

  • Pipeline parameter disableDTE works correctly
  • Pipeline doesn't fail if Nx Cloud is temporarily unavailable
  • Agent jobs exit gracefully when orchestrator finishes

Verification Commands

# Build passes
pnpm nx build api
pnpm nx build web

# Tests pass
pnpm nx run-many -t test --all --exclude=api-e2e,acceptance-tests

# Lint passes
pnpm nx run-many -t lint --all

# Remote caching works (second run should be instant)
pnpm nx run-many -t build --all
pnpm nx run-many -t build --all  # Should show cache hits

# Verify Nx Cloud connection
pnpm nx-cloud verify

🚫 Constraints and Rules

MUST DO

  • Store NX_CLOUD_ACCESS_TOKEN as a secret — never commit to repository
  • Keep Docker packaging jobs as standalone Azure jobs (not DTE-managed)
  • Keep all deployment stages completely unchanged
  • Provide PostgreSQL on every DTE agent that might run test tasks
  • Preserve test result publishing to Azure DevOps
  • Add disableDTE parameter as a safety escape hatch
  • Run prisma generate on all agents (needed for API lint/test/build)

MUST NOT

  • Expose staging/production secrets to Nx Cloud agents
  • Remove the existing pipeline structure for deployment stages
  • Use Nx Cloud Hosted Agents for tasks requiring PostgreSQL (use manual DTE)
  • Hard-code the Nx Cloud access token in nx.json or pipeline YAML
  • Skip the fallback mechanism — CI must work even if Nx Cloud is down
  • Change the Docker packaging strategy (multi-stage builds are already optimized)

🔄 Migration Strategy

To avoid disruption, implement in stages:

  1. Week 1: Connect Nx Cloud + enable remote caching only (no DTE). All existing pipeline stages remain. Verify cache hits across branches.
  2. Week 2: Add DTE for feature branches only. Main branch keeps existing stages but gains remote cache. Monitor for issues.
  3. Week 3: Add DTE for main branch Validate + Test. Docker packaging remains as-is. Tune agent count based on observed performance.

This approach allows rolling back at any stage if issues arise.


END OF PROMPT


This prompt integrates Nx Cloud with Distributed Task Execution into the Forma3D.Connect Azure DevOps pipeline. The AI should connect the workspace to Nx Cloud, configure manual DTE with Azure agent jobs, restructure the pipeline stages for maximum parallelism, and preserve all Docker packaging and deployment functionality. Remote caching and distributed execution should reduce feature branch CI time by ~2.5–3× and main branch CI time by ~2×.