How do I add a transient field to a list grid?

We can achieve this by extending the controller and overriding the modifyEntityForm(...). Once this is setup, you'll be able to grab the Tab where your ListGrid lives from the EntityForm and pull the ListGrid off of it.

Something like this should work (adjusted to your ListGrid on your Controller):

    @Override
    protected void modifyEntityForm(EntityForm entityForm, Map<String, String> pathVars) throws Exception {
        super.modifyEntityForm(entityForm, pathVars);
        
	ListGrid yourGrid = entityForm.findListGrid("yourGrid");
        if (yourGrid != null) {
            Field TransientField = new Field()
                    .withName("Transient Header")
                    .withFriendlyName("Transient Header")
                    .withFieldType(SupportedFieldType.STRING.toString())
                    .withOrder(Integer.MAX_VALUE);
            yourGrid.getHeaderFields().add(TransientField);

            for (ListGridRecord record: yourGrid.getRecords()) {
		// Logic to retrieve your transient value		
		String someValue = "Some Value";
                Field endTimeField = new Field()
                        .withName("Transient Name")
                        .withFriendlyName("Transient Header")
                        .withFieldType(SupportedFieldType.STRING.toString())
                        .withValue(someValue)
                        .withDisplayValue("Some Value")
                        .withFriendlyName("Transient Name")
                        .withOrder(Integer.MAX_VALUE);
                record.getFields().add(endTimeField);
		record.clearFieldMap();
            }
        }
    }

One key thing to note is the ordering. While clearing the FieldMap will reset the ordering on the record fields there can be situations where the HeaderFields will ultimately define the ordering first. In this case, simply setting the order will NOT work. This is because the headerFields is a TreeSet that is ordered very specifically when fields get added. Since the fields are then changing after the fact, it is not getting ordered correctly. 

When the headerFields get created it set with a Comparator:

protected Set<Field> headerFields = new TreeSet<Field>(new Comparator<Field>() {        

	@Override
        public int compare(Field o1, Field o2) {
            return new CompareToBuilder()
                    .append(o1.getOrder(), o2.getOrder())
                    .append(o1.getFriendlyName(), o2.getFriendlyName())
                    .append(o1.getName(), o2.getName())
                    .toComparison();
        }
    });

Considering that, the best way to do keep the ordering needed we need to do one of the following options:

A) Alter the headerFields TreeSet by removing the field, changing the order on the field, and then adding it back in.
B) Create a new TreeSet with the same Comparator, add all the fields in after changing the order and then setting the new TreeSet on the ListGrid.