Original text:
Commit message. An Add is applied after being included in a Commit message.  The <del> position of the Add in the list of adds determines the leaf index "index" where the new member will be added.  For the first Add in the Commit, "index" is the leftmost empty leaf in the tree, for the second Add, the next empty leaf to the right, etc. </del>  If necessary, extend the tree to the right until it has at least index + 1 leaves the sender of the proposal SHOULD retransmit it in the new epoch. A member of the group MAY send a Commit that references no proposals <del> at all, which would thus have empty vectors for "updates", "removes", and "adds".  Such a Commit resets the sender's leaf and the nodes along its direct path, and provides forward secrecy and post- compromise security with regard to the sender of the Commit.  An Update proposal can be regarded as a "lazy" version of this operation, where only the leaf changes and intermediate nodes are blanked out. </del>  The "path" field of a Commit message MUST be populated if the Commit <del> covers at least one Update or Remove proposal, i.e., if the length of the "updates" or "removes" vectors is greater than zero.  The "path" field MUST also be populated if the Commit covers no proposals at all (i.e., if all three proposal vectors are empty).  The "path" field MAY be omitted if the Commit covers only Add proposals.  In pseudocode, the logic for whether the "path" field is required is as follows: </del>  To summarize, a Commit can have three different configurations, with different uses: A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps: <del> Construct an initial Commit object with "updates", "removes", and "adds" fields populated from Proposals received during the current epoch, and empty "key_package" and "path" fields. </del>  Generate a provisional GroupContext object by applying the <del> proposals referenced in the initial Commit object in the order provided, as described in proposals.  First the list of update proposals, then the list of remove proposals, and last the list of add proposals.  Add proposals are applied left to right: Each Add proposal is applied at the leftmost unoccupied leaf, or appended to the right edge of the tree if all leaves are occupied. </del>  Decide whether to populate the "path" field: If the "path" field is required based on the proposals that are in the commit (see tree indicated by the "sender" field. Generate a provisional GroupContext object by applying the <del> proposals referenced in the commit object in the order provided, as described in proposals.  First the list of update proposals, then the list of remove proposals, and last the list of add proposals.  Add proposals are applied left to right: Each Add proposal is applied at the leftmost unoccupied leaf, or appended to the right edge of the tree if all leaves are occupied. Verify that the "path" value is populated if either of the "updates" or "removes" vectors has length greater than zero, or if all of the "updates", "removes", and "adds" vectors are empty. </del>  Otherwise, the "path" value MAY be omitted. If the "path" value is populated: Process the "path" value using

Reviewer comments:
In principle, the set of ProposalType values is extensible. But with the current Commit syntax, there is no way for a Commit to reference a Proposal with a type other than Add/Update/Remove. We could enable this by adding a field to the commit that would carry such references: However, if other proposal types had sequencing requirements, they would not get the benefit of explicit, separate grouping and ordering as is done for the current proposal types. If we don't extend the Commit in this way, we should add some explanatory text to clarify that new proposal types require an extension that changes the format of the Commit message.
Personally, I lean toward the "do nothing" option here. If we take the view that proposals should describe modifications to the tree (other actions being signaled in ), then Add, Update, and Remove are pretty much the available atomic operations. You could envision some minor variations (an Add with a specified location, say) or compositions (Resync = Remove+Add), but nothing is coming to my mind that would be important enough to merit the extra complexity.
Fixed by
[This issue is part of a feature breakdown based on ] Applications might want to signal to group members processing a commit that they should take certain actions in addition to ratcheting the group forward. The most obvious example is folding in a PSK for the epoch, but you can also imagine addressing things like transcript consistency or updatable PKE through this channel. This should be straightforward syntactically, but will also require updating the extensibility section to make clear that Commit extensions can also in principle affect the GroupContext extensions. In other words: Each group member associates a list of extensions with the group, which are confirmed via the GroupContext New joiners get configured with these via extensions in the GroupInfo Existing members can find out about changes to the group's extensions via extensions in the Commit
I think making commit messages extensible is a great idea. However, I'd like to understand better what we want to use the extensions first. I thought that was what Proposals are for. I think for mechanisms that are part of the base protocol spec, there is no reason not to extend the list of proposals. Regarding the nature of extensions: Do I understand correctly, that an extension is a property of the group first and foremost? And extensions in the commit are meant to alert other group members to changes in those properties? Or are extensions in the commit messages a mechanism to allow for the inclusion of additional data in the commits? Or both? I feel like the meaning of the word "extension" is a little stretched here. Since we're discussing this also in the context of PSK injections: Do you think PSK injections should be something that can be switched on and off for any given group? If so, why? I would consider it part of the base protocol and do not see what benefits we get from disallowing it in a group.
It's a fair point, that we have basically two extension points here -- proposal types and extensions. I think the theory I had in mind was something like: Proposals describe modifications you make to the tree before you use it for TreeKEM (to get the path) (if you do) The DirectPath describes the outcome of TreeKEM.Encap Extensions tell you anything else you need to do If that philosophy makes sense, I can update the PR to make that clearer. Note that proposal extensibility is currently broken because we don't actually have a way for the Commit to include any Proposals with new proposal types. I've filed for that.
On the nature of extensions: My thinking here is that an "extension" is purely syntactic; its semantics are defined entirely by its ExtensionType. One thing that extensions do (in the context of GroupContext) is express what attributes/parameters the group has agreed to. But they can also do other things.
On PSK injections: My thinking here is that PSKs are a per-epoch decision, in that you can inject a different set of PSKs at each epoch. Since the Commit is the thing that starts a new epoch, it signals what PSKs to use for that epoch. A few illustrative cases: A "meeting password" might get injected on every commit ... or you might only inject it in commits where you have an Add, to verify that the new guy has it In a scenario where you have some OOB PSK distribution (say to add more PQ safety), you would inject a PSK every epoch, but a different one in each epoch When re-starting a group or branching a subgroup, you would incorporate the resumption secret from the previous group, but then might not need a PSK ever again.
I see, thanks for the clarification. My understanding of extensions was that their advantage is that they're dynamic and can be defined and used after MLS is defined, i.e. to keep the standard extensible. I know that in the TLS context, extensions are used for all sorts of basic protocol behaviour, but my understanding was that was more something that has evolved over time rather than it being "by-design". Is there any reason for Proposals to be limited to TreeKEM operations (other than that being their historic purpose)? Because otherwise it seems a little odd to me not to use them for core MLS operations such as PSK injection.
Otherwise, I agree with your take on PSK injections. They should definitely be part of the commit and it should be possible to inject them ad-hoc. Although for the re-starting or branching use-case, I think they should be part of the Welcome message, but I think we're having that discussion on the mailing list already.
This was discussed at the interim on 2020-08-25. The feeling of the group was that Proposals should be a sufficient extension mechanism for Commits, so we don't need this if we And since is about to do that, closing this issue.
Two small things, otherwise lgtm.

Revised text:
Commit message. An Add is applied after being included in a Commit message.  The  <ins> position of the Add in the list of proposals determines the leaf index "index" where the new member will be added.  For the first Add in the Commit, "index" is the leftmost empty leaf in the tree, for the second Add, the next empty leaf to the right, etc. </ins> If necessary, extend the tree to the right until it has at least index + 1 leaves the sender of the proposal SHOULD retransmit it in the new epoch. A member of the group MAY send a Commit that references no proposals  <ins> at all, which would thus have an empty "proposals" vector.  Such a Commit resets the sender's leaf and the nodes along its direct path, and provides forward secrecy and post-compromise security with regard to the sender of the Commit.  An Update proposal can be regarded as a "lazy" version of this operation, where only the leaf changes and intermediate nodes are blanked out. </ins> The "path" field of a Commit message MUST be populated if the Commit  <ins> covers at least one Update or Remove proposal.  The "path" field MUST also be populated if the Commit covers no proposals at all (i.e., if the proposals vector is empty).  The "path" field MAY be omitted if the Commit covers only Add proposals.  In pseudocode, the logic for validating a Commit is as follows: </ins> To summarize, a Commit can have three different configurations, with different uses: A member of the group creates a Commit message and the corresponding Welcome message at the same time, by taking the following steps:  <ins> Construct an initial Commit object with the "proposals" field populated from Proposals received during the current epoch, and empty "key_package" and "path" fields. </ins> Generate a provisional GroupContext object by applying the  <ins> proposals referenced in the initial Commit object, as described in proposals.  Update proposals are applied first, followed by Remove proposals, and then finally Add proposals.  Add proposals are applied in the order listed in the "proposals" vector, and always to the leftmost unoccupied leaf in the tree, or the right edge of the tree if all leaves are occupied. Note that the order in which different types of proposals are applied should be updated by the implementation to include any new proposals added by negotiated group extensions. </ins> Decide whether to populate the "path" field: If the "path" field is required based on the proposals that are in the commit (see tree indicated by the "sender" field. Generate a provisional GroupContext object by applying the  <ins> proposals referenced in the initial Commit object, as described in proposals.  Update proposals are applied first, followed by Remove proposals, and then finally Add proposals.  Add proposals are applied in the order listed in the "proposals" vector, and always to the leftmost unoccupied leaf in the tree, or the right edge of the tree if all leaves are occupied. Note that the order in which different types of proposals are applied should be updated by the implementation to include any new proposals added by negotiated group extensions. Verify that the "path" value is populated if the "proposals" vector contains any Update or Remove proposals, or if it's empty. </ins> Otherwise, the "path" value MAY be omitted. If the "path" value is populated: Process the "path" value using