Buildtree v3: Common issues and solutions

This page outlines common issues encountered by developers working with Buildtree v3, particularly those learning from existing specifications written for earlier versions. Each section provides a concise explanation of the issue with practical examples.

Debugging tip: You can add ?buildtree=vX (where X=1,2,3) to the policy editor URL to temporarily override the Buildtree version and see how a policy looks under a different version.

UI display changes

Datapoint ordering in UI components

Buildtree v3 changes the way datapoints are ordered within UI components. Earlier versions displayed datapoints in source order (the order in which they appear in the specification), whilst v3 displays them in record order (the order in which they appear in the submission record definition). This affects the display sequence of form fields and other UI elements derived from Brossa specifications.

For example, consider the following Brossa specification:

@buildtree(v1)
a: Text;
b: Text;
submission = { b, a };

In Buildtree v1:

  • UI displays datapoints in order: a, b.

In Buildtree v3:

  • UI displays datapoints in order: b, a.

Function call datapoint restrictions

Buildtree v3 changes how function call datapoints are displayed and handled in the UI.

For example, consider the following specification:

bar(id: Text): Text;
baz: Text;
submission = { foo: bar(baz) };

In Buildtree v1:

  • Only baz is displayed initially.

  • bar(baz) appears once baz is filled in.

In Buildtree v3:

  • Both baz and bar fields are always displayed.

  • bar will not save data until baz is filled in.

Lifted restrictions

Grouped datapoint naming restrictions

Buildtree v3 lifts a restriction from earlier versions regarding grouped datapoint naming. In v1, grouped datapoints had to have the group name as a prefix to be included in the group, whilst v3 allows any datapoint to be grouped regardless of its name.

For example, consider the following specification:

plain.thing: Text;
group.thing: Text;

group = {
  plain.thing,
  group.thing,
};
group has heading "Group";

In Buildtree v1:

  • plain.thing appears outside the group (no matching prefix).

  • group.thing appears in the group (prefix matches group name).

In Buildtree v3:

  • Both plain.thing and group.thing appear in the group.

Multiple datapoint references

Buildtree v3 lifts a restriction from earlier versions regarding multiple references to the same datapoint. In v1, a datapoint explicitly referenced in the goal record could only appear once in the UI, while v3 allows datapoints mentioned multiple times to appear multiple times in the UI.

For example, consider the following specification:

thing: Text;

group = {
  thing,
};
group has heading "Group";

submission = { thing, group };

In Buildtree v1:

  • thing appears only once at the top level.

  • The group appears empty.

In Buildtree v3:

  • thing appears twice: once at the top level and once in the group.

Datapoints in multiple subforms

Buildtree v3 lifts a restriction from earlier versions regarding datapoint uniqueness across subforms. In v1, each datapoint was unique on the page, while v3 allows top level datapoints to appear in multiple subforms.

For example, consider the following specification:

thing: Text;
subform.ids: #{Uuid};

submission = {
  subform: [thing | id <- subform.ids],
};

In Buildtree v1:

  • Each datapoint appears only once on the page.

In Buildtree v3:

  • thing appears in each subform when multiple subforms are added in the UI.

Nested subforms

Buildtree v3 lifts a restriction from earlier versions by supporting nested subforms in the UI.

For example, in v3 the following specification will display sections-in-layers as expected:

sections.ids(layer_id: Uuid): #{Uuid};
section.thing(id: Uuid): Text;
section(id: Uuid) = {
  thing: section.thing(id),
};
layers.ids: #{Uuid};
layer(layer_id: Uuid) = [section(id) | id <- sections.ids(layer_id)];

submission = {
  layers: [layer(id) | id <- layers.ids],
};

Syntax changes

Subform definitions

Buildtree v3 changes how subforms must be defined. Earlier versions allowed subforms to be defined as literal lists, whilst v3 requires all subforms to use list comprehension syntax.

For example, the following subform definition works in v3:

my.subform = [stuff(id) | id <- my.subform.ids];

Table syntax

Buildtree v3 supports both legacy and new table syntax approaches, but with important restrictions on the legacy approach.

Legacy list comprehension syntax

The v1 syntax of defining tables via list comprehension is still supported in v3, but with a key restriction: the list comprehension generator must be the ID set of the table. The ID set must also have a table render metakey as in v1.

New generate_table syntax

The generate_table syntax is only available in v3 and completely replaces the need for list comprehension syntax and table render metakeys when used.

New features and tools

Timing information in the policy editor

Buildtree v3 adds a debugging feature that shows per-node timing information in the policy editor. This helps diagnose slow form rendering and evaluation.

You can enable this feature by adding the ?timing=true parameter to the policy editor URL when viewing a policy.

This feature is only available in Buildtree v3. Earlier versions do not evaluate nodes while building the tree, so timing data is not available there.