code: func translateToStepGroups(fc *flow.Flow) []*StepGroup {
  	// use array instead of map to ensure consistent ordering
  	stepId2StepGroup := make([]*StepGroup, len(fc.Steps))
  	for _, step := range fc.Steps {
  		// println("step:", step.Name, step.Id, "starting...")
  		ancestorStepId, foundStepId := findAncestorStepId(step)
  		if !foundStepId {
  			println("step:", step.Id, "Not found ancestorStepId.")
  			continue
  		}
  		// println("step:", step.Name, step.Id, "ancestorStepId", ancestorStepId)
  		if stepId2StepGroup[ancestorStepId] == nil {
  			stepId2StepGroup[ancestorStepId] = NewStepGroup()
  			for _, ds := range step.InputDatasets {
  				parentDsId, hasParentIdId := findAncestorStepId(ds.Step)
  				if !hasParentIdId {
  					// since we add steps following the same order as the code
  					log.Panic("parent StepGroup should already be in the map")
  				}
  				parentSg := stepId2StepGroup[parentDsId]
  				if parentSg == nil {
  					// since we add steps following the same order as the code
  					log.Panic("parent StepGroup should already be in the map")
  				}
  				stepId2StepGroup[ancestorStepId].AddParent(parentSg)
  			}
  		}
  		stepId2StepGroup[ancestorStepId].AddStep(step)
  	}
  	// shrink
  	var ret []*StepGroup
  	for _, stepGroup := range stepId2StepGroup {
  		if stepGroup == nil || len(stepGroup.Steps) == 0 {
  			continue
  		}
  		// println("add step group started by", stepGroup.Steps[0].Name, "with", len(stepGroup.Steps), "steps")
  		ret = append(ret, stepGroup)
  	}
  
  	return ret
  }
docs: // group local steps into one step group