botassembly
You write the procedure in plain English, and agents run it in the background with nobody watching.
Who it is for
You own the work and you do not write code. You describe the procedure step by step, the way you would hand it to a new colleague. The work runs on its own, it repeats, and it waits for nobody.
What it is
An assembly is a folder of plain files. It tells agents what to do, one step at a time. A runtime is the program that executes such a folder. It walks the folder, runs the checks you wrote, and writes down what happened.
Why it holds
Understandable. A person reads the instructions and the record without installing anything. Durable. The files live in git and outlive the program that ran them. Portable. The format names no program.
Twelve files, one run. Step through the folder.
triage/1 file
- ASSEMBLY.mdnew
---
intelligence: default
retries: 1
---
Sorts an inbound customer request into an urgent or a routine queue and writes the routing memo.
1 The folder
An assembly is a folder. ASSEMBLY.md is the one file it must hold. The front matter sets the options every stage inherits, and the body says what the work is. Nothing registers the folder anywhere else.
triage/2 files
- ASSEMBLY.md
- flows/triage/
- FLOW.mdnew
---
description: sort a customer request into urgent or routine and write the routing memo
tmp: flow
---
One customer request travels the whole flow. Decide its queue, write the memo, then verify the memo.
2 The flow
A flow is a folder of numbered steps. FLOW.md carries the one required key, description, which is the line a routing decision reads. The listing order is the run order. No graph file records the sequence.
triage/3 files
- ASSEMBLY.md
- flows/triage/
- FLOW.md
- 01-classify/
- STAGE.mdnew
---
timeout: 300
---
Read the customer request in $INPUT. Read the rubric at
$SKILLS/priority-rubric/SKILL.md. Write to $OUTPUT a JSON object with keys
`priority` ("urgent" or "routine"), `trigger` (the exact phrase from the
request that decided it), and `request` (the request text).
## Checklist
- The priority follows the rubric at $SKILLS/priority-rubric/SKILL.md
- The trigger phrase appears verbatim in the request
3 A stage
A stage is one file. The body is the prompt the agent gets. $INPUT, $OUTPUT, and $SKILLS are slots, and the runtime fills each one with a real path before the agent reads it. The fence at the top is the configuration: this stage gives itself five minutes and takes everything else from the assembly. The keys it may set are fixed, and an unknown one refuses the whole folder.
triage/3 files
- ASSEMBLY.md
- flows/triage/
- FLOW.md
- 01-classify/
- STAGE.md
---
timeout: 300
---
Read the customer request in $INPUT. Read the rubric at
$SKILLS/priority-rubric/SKILL.md. Write to $OUTPUT a JSON object with keys
`priority` ("urgent" or "routine"), `trigger` (the exact phrase from the
request that decided it), and `request` (the request text).
## Checklist
- The priority follows the rubric at $SKILLS/priority-rubric/SKILL.md
- The trigger phrase appears verbatim in the request
4 The checklist
The checklist is the first of three checks. The agent affirms one item at a time and gives evidence for each. A stage cannot end while an item stands unaffirmed.
triage/4 files
- ASSEMBLY.md
- flows/triage/
- FLOW.md
- 01-classify/
- STAGE.md
- schema.jsonnew
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["priority", "trigger", "request"],
"additionalProperties": false,
"properties": {
"priority": { "enum": ["urgent", "routine"] },
"trigger": { "type": "string", "minLength": 1 },
"request": { "type": "string", "minLength": 1 }
}
}
5 The schema
The schema is the second check. The output must be an object carrying priority, trigger, and request, and nothing else. The next stage relies on that shape.
triage/7 files
- ASSEMBLY.md
- flows/triage/
- FLOW.md
- 01-classify/
- STAGE.md
- schema.json
- 03-verify/
- STAGE.mdnew
- gate/
- 01-blockerexecnew
- 02-sectionsexecnew
#!/bin/sh
# Refuse a memo that lost a required section. The gate's own words are
# what reaches the agent on a send-back and the terminal on exhaustion.
set -eu
for heading in "## Queue" "## Why" "## Action"; do
if ! grep -qF "$heading" "$1"; then
echo "The memo is missing its $heading section. Add it."
exit 1
fi
done
6 The gates
A gate is the third check. It is a script that runs after the stage and reads what the stage wrote. Exit 0 passes the work on. Any other exit sends the work back to the agent, and the script's own words are the reason it gets. The gate beside this one, 01-blocker, exits 75, which means something outside the run is unavailable, so the run stops as blocked rather than failed.
triage/10 files
- ASSEMBLY.md
- flows/triage/
- FLOW.md
- 01-classify/
- STAGE.md
- beforeexecnew
- failureexecnew
- schema.json
- successexecnew
- 03-verify/
- STAGE.md
- gate/
- 01-blockerexec
- 02-sectionsexec
#!/bin/sh
# before hook — append one line to the run log when a log is configured.
# Set BOT_HOOK_LOG to a writable file to collect the three hook lines.
set -eu
[ -n "${BOT_HOOK_LOG:-}" ] || exit 0
printf '%s\t%s\tCAUSE=%s\tREASON=%s\tINPUT=%s\tOUTPUT=%s\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "before" "${CAUSE:-}" "${REASON:-}" "${INPUT:-}" "${OUTPUT:-}" \
>> "$BOT_HOOK_LOG"
7 The hooks
Hooks run around a stage. They log, notify, or prepare. The record keeps each hook exit code. A hook never rejects the work.
triage/11 files
- ASSEMBLY.md
- flows/triage/
- FLOW.md
- 01-classify/
- STAGE.md
- beforeexec
- failureexec
- schema.json
- successexec
- 03-verify/
- STAGE.md
- gate/
- 01-blockerexec
- 02-sectionsexec
- skills/priority-rubric/
- SKILL.mdnew
---
description: the two-tier rubric the support desk uses to sort customer requests into urgent and routine
---
# Priority rubric
A request is **urgent** when it names any of: a deadline inside three working
days, money taken for something not delivered, an account nobody can sign in
to, or anything unsafe.
A request is **routine** for everything else, including billing questions,
plan changes, feature requests, and how-to questions.
8 The skill
A skill is reference material the stage reaches through $SKILLS. The two-tier priority rubric is written down once here. Changing the policy is one edit, and no prompt repeats it.
triage/14 files
- ASSEMBLY.md
- flows/triage/
- FLOW.md
- 01-classify/
- STAGE.md
- beforeexec
- failureexec
- schema.json
- successexec
- 02-route/
- CHOOSE.mdnew
- routine/
- 01-routine.mdnew
- urgent/
- 01-urgent.mdnew
- 03-verify/
- STAGE.md
- gate/
- 01-blockerexec
- 02-sectionsexec
- skills/priority-rubric/
- SKILL.md
---
timeout: 120
---
Read the classification in $INPUT and pick the queue it belongs in.
- `urgent` — the classification says priority is urgent
- `routine` — the classification says priority is routine
9 The choice
A folder can carry the control flow. CHOOSE.md in a folder means the agent picks one child folder and the others do not run. Moving a folder changes the branching, and no other file records it.
triage/ now holds 14 files: one flow, 5 stage files, a skill, a schema, two gates, and three hooks. A run walks 4 of the stages, because the choice takes one arm and declines the other.
flows/triage/FLOW.md flow-definition type=FLOW flow=flows/triage max_subflow_calls=10
01-classify STAGE flow=flows/triage input=request.txt output=classify.json options=intelligence=default,provider=google,model=gemini-3.5-flash-lite,reasoning=low,timeout=300,retries=1,local-context=ignoreoptions=…
02-route CHOOSE flow=flows/triage input=- output=- options=intelligence=default,provider=google,model=gemini-3.5-flash-lite,reasoning=low,timeout=120,retries=1,local-context=ignoreoptions=…
02-route/routine/01-routine STAGE flow=flows/triage input=classify.json output=routine.txt options=intelligence=default,provider=google,model=gemini-3.5-flash-lite,reasoning=low,timeout=120,retries=2,local-context=ignoreoptions=…
02-route/urgent/01-urgent STAGE flow=flows/triage input=classify.json output=urgent.txt options=intelligence=default,provider=google,model=gemini-3.5-flash-lite,reasoning=low,timeout=120,retries=2,local-context=ignoreoptions=…
03-verify STAGE flow=flows/triage input=routine.txt output=verify.txt options=intelligence=default,provider=google,model=gemini-3.5-flash-lite,reasoning=low,timeout=3600,retries=1,local-context=ignore possible_inputs=routine.txt,urgent.txtoptions=…
$ echo $?
0
10 Check it
bot assembly check calls no model and costs nothing. It prints one flow-definition row and one row for each reachable node. Six rows appear here. Two node rows are the choice arms, so a run walks four nodes.
triage/ now holds 14 files: one flow, 5 stage files, a skill, a schema, two gates, and three hooks. A run walks 4 of the stages, because the choice takes one arm and declines the other.
Synthetic illustration. Authored illustration for this repository. These values do not describe a provider run.
$ bot run start ./triage/triage @data/request-urgent.txt
run_startsynthetic-run · flow triage · request.txt
01-classify
- hook beforeexit 0
- mark item 1 doneThe request states a near deadline.
- mark item 2 doneThe priority rule applies.
- check outputexit 0
- check checklistexit 0
- check schemaexit 0
- hook successexit 0
- stage_end successexit 0
02-route
- chose urgent, declined routineThe synthetic request uses the urgent rule.
- stage_end successexit 0
02-route/urgent/01-urgent
- check outputexit 0
- stage_end successexit 0
03-verify
- check outputexit 0
- gate 01-blockerexit 0
- gate 02-sectionsexit 0
- stage_end successexit 0
run_endexit 0success
11 The run
One command runs the flow. Every stage passes through the checks you just read, and the runtime writes each event as it happens. The display uses authored synthetic events, not a measured provider run.
triage/ now holds 14 files: one flow, 5 stage files, a skill, a schema, two gates, and three hooks. A run walks 4 of the stages, because the choice takes one arm and declines the other.
Synthetic illustration. Authored illustration for this repository. These values do not describe a provider run.
- run
synthetic-run- flow
triage- events
- 22 lines of
record.jsonl - stages
- 4, every one exit 0
- exit
- exit 0 success
A run folder holds
assembly/record.jsonlrequest.txtstages/
Illustrative memo headings
- ## Queue
- ## Why
- ## Action
This repository does not publish provider records or sessions. They stay local because requests, outputs, tool results, and inherited values can be sensitive.
12 The record
A run leaves a local folder behind. Its record.jsonl holds ordered events, stage outcomes, checks, usage, and the final exit. Reading it needs no provider connection. The illustration is synthetic. Actual records and sessions stay local because they can contain sensitive values.