/** * 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//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. `;