code: func (bp *BuildPage) Steps() []*Step {
  	if bp.steps != nil {
  		return bp.steps
  	}
  	collapseGreen := bp.StepDisplayPref == StepDisplayDefault
  	// Use a map to store all the known steps, so that children can find their parents.
  	// This assumes that parents will always be traversed before children,
  	// which is always true in the build proto.
  	stepMap := map[string]*Step{}
  	for _, step := range bp.Build.Steps {
  		i := common.ToInterval(step.GetStartTime(), step.GetEndTime())
  		i.Now = bp.Now
  		s := &Step{
  			Step:      step,
  			Collapsed: collapseGreen && step.Status == buildbucketpb.Status_SUCCESS,
  			Interval:  i,
  		}
  		stepMap[step.Name] = s
  		switch nameParts := strings.Split(step.Name, "|"); len(nameParts) {
  		case 0:
  			panic("Invalid build.proto: Step with missing name.")
  		case 1:
  			// Root step.
  			bp.steps = append(bp.steps, s)
  		default:
  			parentName := step.Name[:strings.LastIndex(step.Name, "|")]
  			parent, ok := stepMap[parentName]
  			if !ok {
  				panic("Invalid build.proto: Missing parent.")
  			}
  			parent.Children = append(parent.Children, s)
  		}
  	}
  	return bp.steps
  }
docs: // Steps converts the flat Steps from the underlying Build into a tree.
  // The tree is only calculated on the first call, all subsequent calls return cached information.
  // TODO(hinoka): Print nicer error messages instead of panicking for invalid build protos.