- name
- sheet-design
- description
- How to build a working sheet — the exact sheet.json contract, and how to design columns as a left-to-right pipeline where an agent column runs another agent once per row. Use for EVERY sheet request, before writing anything.
# Sheet design
## The rules that make this work here
These travel with the Skill so that a sheet made on any Harness that has it behaves the same.
- THE FILE: `./sheet.json`, in your current working directory. That exact path, always. Do not
search for it and do not treat its absence as a puzzle: on a new sheet it does not exist yet and
you create it there. It is the single source of truth and the only file the app reads.
- `sheet.json` IS the deliverable. Never delete it or replace it with a CSV, XLSX or any other
export unless the person asks for that format by name, and then keep `sheet.json` beside it.
- Read it before every change and write it back WHOLE: the person may have edited the grid between
turns. Validate with this Skill's `validate_sheet.py` before you finish.
- A column may be an AGENT column (type "harness"): it runs an agent once per row. You create and
configure such a column, but you NEVER execute one; the app does, and it will not let a sheet
run itself.
- ALWAYS give an agent column a working default so the person can press Run the moment the sheet
exists. Set harness_id to a BASE agent id that suits the work: "codex", "claude-code", "hermes",
"pi", "dsh", "opencode", "qwen". Never invent a chrn_ id; you cannot see the person's own agents,
and an invented id silently runs the wrong one. If the base you pick is not installed, the app
substitutes one that is, so a reasonable choice beats a blank.
- Cells in an agent column carry results the app produced: status, session_id, response_id,
artifacts. Never write them and never delete them unless asked to clear that column; you cannot
recompute them.
- Work directly. Every command you spend orienting is a command the person waits through.
You are designing a pipeline that happens to look like a spreadsheet.
## The file you are writing
There are no sheet tools here. A sheet is ONE file — `sheet.json` in your
working directory — and you write it with the ordinary file editor. Nothing
else reads it, so a file that does not match this shape shows the person an
empty grid or a column with no editor. Match it exactly.
```json
{
"meta": { "schema": 1, "title": "Competitor scan" },
"columns": [
{ "id": "col_company", "name": "Company", "type": "text", "width": 200 },
{ "id": "col_site", "name": "Site", "type": "url", "width": 240 },
{ "id": "col_brief", "name": "Brief", "type": "harness", "width": 380,
"harness": {
"harness_id": "codex",
"prompt": "Read {{Site}} and write three sentences on {{Company}}: what they sell, who to, and how they price. Put your full notes in notes.md.",
"attach": []
} },
{ "id": "col_fit", "name": "Overlap", "type": "harness", "width": 300,
"harness": {
"harness_id": "codex",
"prompt": "Score 1-5 how directly this company competes with us, then one line of why.\n\n{{Brief}}",
"attach": ["col_brief"]
} }
],
"rows": [ { "id": "row_1" }, { "id": "row_2" } ],
"cells": {
"row_1:col_company": { "value": "Northwind Analytics" },
"row_1:col_site": { "value": "https://northwind.example" }
}
}
```
Non-negotiable, because each of these breaks silently:
- **`cells` is keyed `"<rowId>:<colId>"`**, using ids that exist in `rows` and
`columns`. A key containing a column *name* renders nothing at all.
- **A cell is an object** — `{"value": …}` — never a bare value at the key.
- **Column names are unique.** Agent columns address columns as `{{Name}}`, and
a duplicate makes that ambiguous.
- **`{{Name}}` may only name a column EARLIER in `columns`.** The array order
is the execution order. A forward reference makes the run refuse to start.
- **`type` is one of** `text` `number` `select` `tags` `checkbox` `date` `url`
`harness`. Anything else loses its editor.
- **Only a `harness` column carries a `harness` object**, and only it can be run.
- **`harness_id` names a BASE agent**, so the sheet is runnable the moment it
exists. The base ids are stable and you may write them: `codex`,
`claude-code`, `hermes`, `pi`, `dsh`, `opencode`, `qwen`. Pick the one that
suits the work. If it is not installed on this deployment the app substitutes
one that is, so a reasonable guess always beats a blank. Never invent a
`chrn_` id: those are the person's own agents, you cannot see them, and an
invented one silently runs the wrong agent.
- **Never write `status`, `run_id`, `response_id`, `session_id` or `artifacts`
into a cell, and never invent a `run` block.** Those are results the app
produced. Writing them makes the sheet claim a run that never happened; you
cannot recompute them, and deleting them throws away the link to real work.
- **Ids are stable.** Reuse them when you edit; never renumber a sheet.
- **`meta.schema` is `1`.**
Read `sheet.json` before every change and write it back WHOLE. The person may
have edited the grid between your turns, and a partial write loses their work.
## What you do and do not run
An agent column runs an agent once per row. You create and configure such
columns. **You never execute one.** The app runs them, from the browser, in
dependency order — and it will not let a sheet run itself, so there is no id
you could put there that would point back here.
Leave the sheet ready to go. A person who asked for a sheet with agent columns
wants to press Run, not to open every column menu first.
## Designing the columns
The columns are a pipeline, left to right: **identify → gather → judge.**
- **One agent column does one thing.** A column that researches AND scores
fails as one unit and re-runs as one unit, at full cost. Two columns fail and
re-run independently, and the person can see which step went wrong.
- **Ask for one thing, and say how long.** A cell is a table cell. "Three
sentences", "a score 1-5 and one line of why" — not "analyse this company".
- **Long output goes in a file.** Tell the agent to write the detail to a file
and summarise it in a sentence or two. The app shows the file beside the text,
and the next column can attach it with `attach`.
- **A manual column the person fills beats an agent column that guesses.** If
the value is known, make it a `text` column and let them paste it in.
- **Short, distinct, human column names.** Those names are the vocabulary every
agent column interpolates against.
- **Start with a few rows.** The person adds theirs by pasting; twenty rows of
invented example data is twenty rows they have to delete.
## Review pass (mandatory)
The validator ships beside this SKILL.md, in this Skill's own folder; the command below finds it
wherever your harness placed the Skill. From your working directory:
```
python3 "$(find . -path '*/sheet-design/validate_sheet.py' -not -path '*/node_modules/*' 2>/dev/null | head -1)" sheet.json
```
It prints the exact path of anything that will break, and what to write instead.
**Fix and re-run until it exits clean.** A sheet that fails this shows the
person an empty grid, and you cannot see that from here.
Then reread what you wrote, as the person who has to run it:
- Is every `{{Name}}` a column to its left?
- Does each agent column ask for one thing, in a cell-sized answer?
- Would you know, from the column names alone, what this sheet is for?
Finally, tell the person in one line what to do next — usually: pick the agent
for each agent column in its ⋯ menu, paste their rows, and press Run.
Ver no GitHub