Small one, found while deciding where the immutability of an order line lives. The two neighbouring doc comments in schema/field.go disagree about their own scope:
// ReadOnly makes the column unwritable through REST.
func (f *Field) ReadOnly() *Field
// Immutable allows the column to be set at create time only.
func (f *Field) Immutable() *Field
ReadOnly names its boundary. Immutable reads as a property of the column.
It is not one. Grepping the enforcement, it is REST-only and in two places:
rest/item.go:357,380 — the update path refuses it
codegen/rest.go:123 — it is omitted from the generated …Patch body
sqlb.UpdateRows[T]().SetX(…) from application code writes it, as does a hook, as does an action's write-back. Which is correct — the engine deliberately does not police application writes — but "settable at create time only" is what the sentence says, and the sentence is what someone declaring a financial record reads.
The generated prose inherits the ambiguity and sharpens it, because it drops the qualifier entirely:
codegen/tsclient.go:373 — "Immutable columns are absent: they are settable once, at create."
codegen/gocli.go:704 — "Immutable columns have no flag here: they are settable once at create."
In a client, "settable once, at create" is true and unambiguous — a client has only the REST door. In the schema DSL it is a claim about the column, and the column has two doors.
Why it is worth a line rather than nothing
The repo's own argument against half-closed doors is in docs/concepts/domain-logic.md, and it is the reason I looked:
the problem with it is not that it is more code. It is that the generated door stays open beside it
Immutable is a rule stated on the model, which is the shape that document holds up as the one that closes both doors — and it closes one. That is a fine design; the surprise is that it presents as the other kind. For an immutable financial record the answer turns out to be a trigger regardless, and I would have got there faster from a doc comment that told me Immutable was not going to carry it.
What would fix it
One word, matching its neighbour:
// Immutable makes the column writable through REST at create time only.
Optionally a sentence pointing at where the guarantee lives if you need one — a BEFORE UPDATE trigger — in the same spirit as the four places a rule can live section, which already makes the constraint-versus-convention distinction and is the right frame for this.
No behaviour change wanted. ReadOnly got the qualifier and Immutable did not, and that is the whole of it.
Small one, found while deciding where the immutability of an order line lives. The two neighbouring doc comments in
schema/field.godisagree about their own scope:ReadOnlynames its boundary.Immutablereads as a property of the column.It is not one. Grepping the enforcement, it is REST-only and in two places:
rest/item.go:357,380— the update path refuses itcodegen/rest.go:123— it is omitted from the generated…Patchbodysqlb.UpdateRows[T]().SetX(…)from application code writes it, as does a hook, as does an action's write-back. Which is correct — the engine deliberately does not police application writes — but "settable at create time only" is what the sentence says, and the sentence is what someone declaring a financial record reads.The generated prose inherits the ambiguity and sharpens it, because it drops the qualifier entirely:
codegen/tsclient.go:373— "Immutable columns are absent: they are settable once, at create."codegen/gocli.go:704— "Immutable columns have no flag here: they are settable once at create."In a client, "settable once, at create" is true and unambiguous — a client has only the REST door. In the schema DSL it is a claim about the column, and the column has two doors.
Why it is worth a line rather than nothing
The repo's own argument against half-closed doors is in
docs/concepts/domain-logic.md, and it is the reason I looked:Immutableis a rule stated on the model, which is the shape that document holds up as the one that closes both doors — and it closes one. That is a fine design; the surprise is that it presents as the other kind. For an immutable financial record the answer turns out to be a trigger regardless, and I would have got there faster from a doc comment that told meImmutablewas not going to carry it.What would fix it
One word, matching its neighbour:
// Immutable makes the column writable through REST at create time only.Optionally a sentence pointing at where the guarantee lives if you need one — a
BEFORE UPDATEtrigger — in the same spirit as the four places a rule can live section, which already makes the constraint-versus-convention distinction and is the right frame for this.No behaviour change wanted.
ReadOnlygot the qualifier andImmutabledid not, and that is the whole of it.