Skip to content

docs(mdcode): propose freeform actions - #441

Draft
libei wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
libei:freeform-actions
Draft

libei wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
libei:freeform-actions

Conversation

@libei

@libei libei commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Proposal only — no code. Adds toolbox/mdcode/docs/semantic-model/freeform-actions.md. Supersedes and replaces #440, which is closed; nothing is carried over from it.

An agent in conversation meets write requests no action covers, and declaring more actions never closes that gap — whatever is enumerated, the next sentence is outside it. So enumerate the bounds instead of the writes and generate the statement inside them at call time.

  • A writable surface on the binding profile. Default closed. Written in the same concept / operation / fields vocabulary as AffectedConcept, so admission is a subset test between two values of one type rather than a comparison between a declaration and some SQL. Writability is a physical facet, so it inherits and withdraws exactly as executor and source do.

  • Two-phase, always. propose_change(request, refs) writes nothing and returns what it would do; a person approves; apply_change runs the statements the proposal holds, not a regeneration. A declared action's DML was read by somebody before it shipped — a freeform statement has no such moment, so the moment is the call and the reviewer is the one asking.

  • A six-check AST gate (runtime/admit.ts), using the @polyglot-sql/sdk engine already bundled for transpile.ts. The load-bearing check is that every UPDATE and DELETE must be keyed to a row a lookup already resolved. "Delete all orders" is well-formed DML inside any reasonable surface; what stops it is that no lookup resolved those rows, so there are no keys to bind. That rule is the difference between a bounded surface and a database connection.

  • Constraints attach by what the statement touches, not by a guards list — AnalyzedRule on fork/constraint-eval already reports the entities and fields a rule reads. This reopens the settled rule that a constraint is inert until referenced. The argument for the carve-out is in the doc: that rule protects declared calls from a rule published later, and a freeform write is neither declared nor previously working, so nothing can regress. Declared actions' guards are untouched. Worth settling before anything is built.

  • Graduation. --save-as turns a freeform write that worked into an ordinary sql executor action for review. Freeform is the long tail and the intake path, not a replacement — the catalog grows from what people asked for, and anything recurring stops being generated.

fork/constraint-eval (79709b0) is a prerequisite rather than a neighbour: admission bounds which tables, columns, values and rows, and nothing in it says the resulting state is sound. Its after-timing invariants, which roll back inside the transaction, are the only thing that bounds a statement nobody wrote.

An agent in conversation meets write requests no action covers, and
declaring more actions never closes that gap. This proposes enumerating
the bounds instead of the writes: a 'writable' surface on the binding
profile, default closed, written in the same concept/operation/fields
vocabulary as 'affects' so admission is a subset test between two values
of one type.

A request plus the keys the agent resolved through lookups generates DML,
which is parsed with the polyglot-sql engine already bundled for
transpile.ts. Six AST checks admit it; the load-bearing one requires
every UPDATE and DELETE to be keyed to a row a lookup resolved, so an
unbounded write has no keys to bind. Constraints attach by what the
statement touches rather than by a guards list -- a bounded carve-out to
the rule that a constraint is inert until referenced, argued and flagged
as a decision to make before building.

Nothing writes without a person approving a proposal that names the
change in ontology terms. A write that succeeded can graduate into an
ordinary sql executor action, so the catalog grows from use.

fork/constraint-eval is a prerequisite: its after-timing invariants are
the only thing bounding the state a generated statement leaves behind.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant