Spreadsheet-UX op een echte relationele engine

main

Deze repository wordt als momentopname gepubliceerd: een clone bevat de volledige inhoud, maar geen commitgeschiedenis.

4.7 kB Ruw bestand
/**
 * What an assistant is told about this workspace, in prose.
 *
 * It ships with the manifest rather than living in each person's prompt,
 * because the things it explains are properties of the product and will change
 * when the product does. Anything an assistant has to be told twice belongs
 * here; anything specific to one workspace belongs in the manifest's data.
 */
export const AI_GUIDE = `# Working in a Wellformed workspace

You are reading the manifest of one workspace. It lists every table, its
columns, and the compiled SQL view that goes with it. Read it before writing
anything.

## Reading: SQL

POST /api/w/<slug>/ai/sql with { "sql": "...", "params": [...] }

One statement per call, read-only, enforced by the connection rather than by
inspecting your query. Use "params" for anything that came from a person; the
values are bound, never pasted into the statement.

**Query the \`v_\` views, not the raw tables.** A formula or rollup column is an
expression, not stored data, so it does not exist in the underlying table. The
view for each table has them already compiled, plus the display label of every
link beside its id:

    SELECT "customer::label", "total"
      FROM v_orders
     WHERE "status" != 'Paid'
     ORDER BY "total" DESC

Query the raw table only when you want to see exactly what is stored — usually
before a write, since writes address stored columns.

Every table and column has two names. The *label* is what people call it and
what the grid shows — any words, any language. The *name* is the SQL
identifier, derived from the label at creation and fixed after; it is what SQL,
formulas, filters and every endpoint address. The manifest lists both — write
with \`name\`, and speak to people in labels. When you create a table or column,
send the label and let the server derive the name; never invent identifiers of
your own.

Column names are case-sensitive and quoted with double quotes. Every table has
an \`id\` column, which is what a link column stores.

The catalogue is readable too, as ordinary tables: \`_wellformed_table\`,
\`_wellformed_column\`, \`_wellformed_view\`, \`_wellformed_rule\`, \`_wellformed_audit\`. The manifest
already contains all of it, so reach for these only when you want to query
across them.

## Writing: the REST endpoints, never SQL

You cannot write through /ai/sql, and this is not an obstacle to work around.
A raw INSERT or ALTER TABLE would bypass the catalogue that makes a column
visible to the product, the workflow rules, the row and table limits of the
plan, the guards that refuse to drop a column a formula depends on, and the
audit trail. The endpoints in \`endpoints\` do all of that in the right order.

Use the same key. Everything you do is attributed to the account that made the
key, with the key's label recorded alongside it, and shows up in the audit
trail marked as an agent's work.

## Before you change anything

- Check \`you\` in the manifest for what your key may actually do. \`canWriteRows\`
  and \`canChangeSchema\` are already reduced by the key's ceiling and the
  account's role; do not probe by attempting a write.
- Read before you write. Fetch the rows you intend to change and confirm they
  are the ones you mean, especially when a person described them in words.
- Schema changes are real DDL and are not undoable from the product. Adding a
  column is cheap; dropping one destroys its data. Say what you are about to
  drop, and why, before you drop it.
- Prefer one change at a time over a batch you cannot describe. Every write is
  a separate line in the audit trail, and a person will read it.
- Workflow rules fire on writes and set other columns. Check \`rules\` before
  being surprised by a value you did not set.
- A column's type and its constraints are changed by *tightening* it, and there
  is a dry run for it. Call \`.../tighten/plan\` first, read what it says will
  not fit, and report that before you call \`.../tighten\`. Applying without
  planning is refused anyway when anything does not fit — but the plan is how
  you can say what would be lost before anybody loses it.
- Spellings that read as the same answer — \`c\` beside \`C\` — are merged by
  *normalising*. \`POST .../normalize/plan\` scans the whole table and
  proposes the survivors; show the person what would merge before you call
  \`.../columns/:column/normalize\` with the merges they agreed to. Use the
  plan's spellings, never a fuzzy guess of your own. Stray whitespace is
  below the level of a question: the apply straightens it automatically.

## Reporting back

Quote the SQL you ran. The person using this product can read it — the grid
they are looking at shows the query that drew it, which is the same idea. If a
number is wrong, they should be able to see why from what you showed them.
`;