Research companion to Stateful notes with invokable capabilities. The proposal is not unprecedented; it sits at the intersection of hypermedia actions, schema-generated forms, document actions and trusted controllers.
| System | What is similar | Lesson to borrow |
|---|---|---|
| JSON Hyper-Schema / Hydra / HAL-FORMS | A resource describes links, operations, expected input and possible responses so a generic client can discover valid transitions. | Treat functions as advertised affordances. Include input/output schemas, method/effect, documentation and structured failure outcomes. |
| Coda Packs and buttons | Documents expose parameterised actions through buttons and automations; action parameters become user input controls. | Keep stable machine names separate from editable labels. One manifest should drive both programmatic calls and browser controls. |
| Airtable interface buttons | A record button can update state or start an automation, with permission, visibility and confirmation behaviour. | Add state/permission-dependent availability, disabled reasons, destructive-action confirmation and actor context. |
| JSON Forms / react-jsonschema-form | A JSON Schema describes data while a separate UI schema controls layout and widgets. | Keep authoritative parameter validation separate from optional presentation hints. Always retain a generic form fallback. |
| Kubernetes custom resources and controllers | Typed inert resources are interpreted by separately installed controllers that hold domain logic. | The sheet declares state and a handler reference; reviewed server handlers execute. Do not make sheet content executable. |
| MCP tools and MCP Apps | Tools publish JSON Schema inputs/outputs; Apps can provide interactive HTML interfaces that invoke tools. | The same action schema can support LLM calls, native/HTML forms and later an optional in-chat UI, without creating one MCP tool per sheet function. |
| CouchDB design documents | Documents can define update handlers and validators operating on current document state and caller context. | Useful proof that document-centred server transitions work. Reject its arbitrary embedded-code model; retain revision checks and server validation. |
| Ethereum smart contracts | An address identifies persistent state plus callable transitions; calls produce atomic state changes or revert and return receipts/events. | Borrow explicit interfaces, pinned code identity, atomic failure and receipts—not consensus, gas, currency or public execution. |
| Jupyter notebooks/widgets | A document combines state, executable behaviour and interactive controls. | Borrow rich document interaction and state synchronisation, but not a general kernel with ambient filesystem/network authority. |
1. Use action/affordance as the user-facing concept while retaining “function” as the call identifier. An action is something currently offered by this resource; a function is its stable machine name.
2. Resolve an effective manifest on read. The installed handler registry owns the authoritative schema, result type, effects and version. A sheet may expose that handler, add labels/help and narrow choices, but cannot broaden authority.
{
"name": "move",
"title": "Play move",
"handler": "builtin:chess@1",
"inputSchema": {"type": "object", "properties": {}},
"outputSchema": {"type": "object", "properties": {}},
"uiSchema": {"order": ["colour", "from", "to"]},
"effects": {"write": ["state", "board", "game_log"]},
"enabled": true,
"disabledReason": null,
"confirmation": "state-change",
"possibleErrors": ["wrong_side", "not_your_turn", "illegal_move", "stale_state"]
}
3. Make availability state-dependent but server-computed. The sheet may display enabled and disabledReason, but should not contain an expression language. For chess, move is unavailable after game end; a caller can still attempt it, but the handler remains authoritative.
4. Return a receipt, not merely success: status, typed result, events, old/new revision, state hash, changed blocks and field-addressable validation errors. This supports audit, UI refresh and an LLM correcting a rejected call.
5. Adopt the two-schema UI pattern: JSON Schema for validation and a separate optional UI schema for ordering, widgets, visibility and help. Unknown UI hints must degrade to ordinary controls.
6. Carry caller identity and request identity separately from arguments. Chess colour remains an explicit assertion checked against the authenticated seat and side-to-move; it is not itself identity. Use an idempotency/request key for safe retries.
7. Keep imperative calls and future reconciliation distinct. notes.call performs an explicit transition. A later watcher/controller facility could maintain desired state in the background, but should be a separate capability class and threat model.
8. Version machine identifiers permanently. Display titles and documentation may change; function name, handler identity and schema version must remain stable or migrate explicitly.
JSON Hyper-Schema and Hydra Core Vocabulary — discoverable links and supported operations.
HAL-FORMS — resource-carried templates for actions and form properties.
JSON Forms — schema-driven forms with a separate UI schema.
Coda Pack actions and Airtable interface buttons — document/record actions surfaced as controls.
Kubernetes custom resources — declarative resources interpreted by trusted controllers.
MCP tools and MCP Apps — typed tool calls and optional interactive tool UIs.
CouchDB design documents — document update handlers and validation.
Ethereum smart contracts — persistent state plus callable code.
The strongest design is a synthesis, not a copy: hypermedia-style advertised actions; JSON-Schema validation and form generation; Kubernetes-style trusted handlers; Coda/Airtable-style controls; and Ethereum-style atomic receipts. This preserves the generic-sheet goal while keeping note data inert and authority on the server.