Skip to main content

timon-workflows

Use when a task needs more than one agent run or a check on an agent's work - verify findings, vote, pick the best of several, revise until a gate passes, retry, or run steps in dependency order with timon's flow patterns. Load timon-agents first for agent.run.

Quellinformationen

Repository
joshrotenberg/timon
Letzte Quellaktivität
17. Oktober 2026 um 04:06
Erkannte Sprache von SKILL.md
Englisch
Sterne
0
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
timon-workflows
description
Use when a task needs more than one agent run or a check on an agent's work - verify findings, vote, pick the best of several, revise until a gate passes, retry, or run steps in dependency order with timon's flow patterns. Load timon-agents first for agent.run.
# Workflows with flow Run `timon` as a command of its own. A host that allows only `timon` commands denies the whole line when it has `;`, `&&`, `|`, `$?`, a redirect or a heredoc around timon. Pass multi-line Lua as one single-quoted argument, which may span lines and cannot contain `'`: use `"` for Lua strings, and put code whose text needs an apostrophe in a file run with `timon eval -f FILE`. Read files with `fs.read` in Lua instead of piping them in, and skip `$?`: a failure prints `timon: kind: message` and exits nonzero. `flow` holds patterns over agent runs, commands and plain Lua. A pattern returns a record and never raises for an outcome: - `status` says whether the pattern ran to the end: `ok`, `failed`, `timeout`, `invalid`. - What it concluded is a separate field: `winner`, `accepted`, `kept`. A review that requests changes, a run that failed and an answer that broke its contract stay three different things. - `runs` holds every agent record the steps returned, and `usage` is `{ runs, usd, unknown_cost }`; Codex reports no cost, so its runs count as unknown, not as $0. A step returns an agent record (its `answer`, or its `text`, becomes the value), another pattern's record, or a plain value. Wrap a pattern in `scope(fn, { budget = { runs = N } })` to cap what it spends. ## Gates A gate checks a value. Prefer a deterministic gate when one exists; ask a model when only judgment will do. ```lua local tests = flow.check({ "true" }) -- a command: passes on exit 0 local short = flow.check(function(s) return #s <= 72, "at most 72 characters" end, "fix: a subject") local real = flow.check({ prompt = "Is this finding real?" }, { file = "a.rs", line = 3 }) -- yes_no return tests.passed, short.passed, real.status ``` A gate function returns `true` or `false` (and a reason), `{ passed, reason }`, or an agent record with a `yes_no` or `review` answer. `passed` is nil when the check did not run. ## Verify, then keep what holds ```lua local findings = { { title = "off by one", line = 3 }, { title = "style nit", line = 0 } } local r = flow.verify{ items = findings, checks = { function(f) return f.line > 0, "points at a line" end, { prompt = "Is this finding a real bug? Answer no for style." }, }, keep = "majority", -- or "all", "any" } return #r.kept, r.usage.runs ``` `majority` refutes: it keeps an item unless most of the gates reject it, so a tie keeps it, and a gate that failed to run counts as no rejection. Kept is not verified. `all` keeps an item only when every gate ran and passed; `any` keeps one when at least one gate ran and passed. ```lua local yes = function() return true end local no = function() return false, "not a bug" end local down = function() error("reviewer unavailable", 0) end local function kept(keep, checks) return #flow.verify{ items = { "finding" }, keep = keep, checks = checks }.kept end assert(kept("majority", { yes, no }) == 1) -- a tie keeps assert(kept("majority", { no, down }) == 1) -- one rejection is not most of two gates assert(kept("majority", { down, down }) == 1) -- no gate ran, so none rejected assert(kept("all", { yes, down }) == 0) -- every gate must run and pass assert(kept("any", { no, down }) == 0) -- no gate passed assert(kept("any", { no, yes }) == 1) -- one gate passed ``` `status = "ok"` says the pattern ran to the end, not that a gate accepted anything. Read each item's `passed` and `rejected` counts and its `verdicts` to tell an accepted item from an inconclusive one: ```lua local down = function() error("reviewer unavailable", 0) end local r = flow.verify{ items = { "critical finding" }, checks = { down, down } } local item = r.items[1] local inconclusive = {} for _, v in ipairs(item.verdicts) do if v.status ~= "ok" then inconclusive[#inconclusive + 1] = v.status .. ": " .. v.error.message end end assert(r.status == "ok" and #r.kept == 1 and item.passed == 0 and item.rejected == 0) assert(#inconclusive == 2 and inconclusive[1] == "failed: reviewer unavailable") return inconclusive ``` Use `keep = "all"` when every gate must accept an item; an item no gate could judge is then rejected, not kept. ## Vote ```lua local r = flow.vote{ n = 3, run = function(i) return agent.run{ prompt = "Is the release ready? Voter " .. i, output = "yes_no" } end, } return r.winner, r.agreed, r.tally ``` The vote stops as soon as one key reaches `quorum` (default: a majority of `n`). `key` decides what counts as the same answer; the default reads a `yes_no` answer. ## Best of n ```lua local r = flow.best_of{ n = 3, run = function(i) return ("candidate %d: "):format(i) .. ("x"):rep(i) end, score = function(text) return -#text end, -- deterministic: the shortest wins } return r.winner, r.value ``` `judge = function(values) return index end` picks instead of `score`; it may run an agent. ## Refine until a gate passes ```lua local r = flow.refine{ draft = function() return "feat: add the thing that does the stuff and more besides that" end, check = function(subject) return #subject <= 50, "subjects are at most 50 characters" end, revise = function(subject, verdict, round) return subject:sub(1, 50 - round) end, rounds = 3, } return r.accepted, r.value ``` `revise(value, verdict, round, last)` gets the failed verdict and the last step's agent record, so an agent can revise in its own session with `agent.continue(last, ...)`. ## Pipeline, retry and plan ```lua local p = flow.pipeline({ "src/a.rs", "src/b.rs" }, { { name = "size", run = function(path) return #path end }, { name = "double", run = function(n) return n * 2 end }, }) local attempts = 0 local r = flow.retry(function(attempt, last) attempts = attempt if attempt == 1 then error("flaky", 0) end return "done after " .. last.error.message end, { times = 2 }) local plan = flow.plan{ steps = { find = { run = function() return { "a", "b" } end }, fix = { needs = { "find" }, run = function(deps) return #deps.find end }, } } return p.values[2], r.value, plan.value.fix ``` - `pipeline` runs every item through the stages with no barrier; an item stops at its first stage that is not ok. - `retry` calls again on `failed`, `timeout` or `invalid`; `last.error.data` holds an invalid answer to repair from. - `plan` starts each step when the steps it `needs` succeed; the first failure stops new steps. ## As tasks A workflow is a task or a script. The review a team runs every day: ```lua fragment -- .timon/tasks.lua review = { description = "Review the branch, then keep the findings both verifiers think are real", budget = { runs = 8, usd = 2 }, run = function() local review = agent.run{ agent = "reviewer", prompt = "Review the diff against main", output = "review" } if review.status ~= "ok" then return review end local r = flow.verify{ items = review.answer.findings, keep = "all", checks = { { prompt = "Is this finding real? Read the code it names." }, function(f) return git.diff{ paths = { f.file } } ~= "", "the file changed on this branch" end, } } return { kept = r.kept, dropped = #r.rejected, usd = r.usage.usd + review.usage.usd } end, }, ``` ## From a sequence to a workflow Offer a workflow once you run the same sequence of agent work and checks on a second input. Bug reports, audited by hand: reproduce one with a failing test, check the test fails and shows that bug, report. The offer is a script in the project's `.timon/scripts/`; kept after the person agrees, the steps and the success criteria are fixed and the report is the input: ```lua fragment -- .timon/scripts/audit.lua -- Reproduce a bug report with a failing test, verify it, report: timon audit REPORT.md local report = fs.read(arg[1]) phase("reproduce") local repro = agent.run{ access = "write", worktree = true, output = "investigation", prompt = "Reproduce this bug with a failing test. Do not fix it.\n\n" .. report, } if repro.status ~= "ok" or not (repro.worktree and repro.worktree.kept) then return { reproduced = false, record = repro.id } -- failed, or wrote no test end local dir = repro.worktree.path local function a_test_fails() local t = proc.run({ "cargo", "test" }, { cwd = dir }) if t.code == 0 then return false, "every test passes" end if not t.out:find("test result: FAILED", 1, true) then return false, "cargo test exited " .. t.code .. " before a test failed" end return true, t.out:match("test %S+ %.%.%. FAILED") or "a test fails" end phase("verify") local r = flow.verify{ items = { repro.answer }, keep = "all", checks = { a_test_fails, { cwd = dir, prompt = "Does the new failing test here show this bug and no other?\n\n" .. report }, } } return { reproduced = #r.kept == 1, -- both gates ran and passed branch = repro.worktree.branch, -- the test, to review or build the fix on verdicts = r.items[1].verdicts, -- why, gate by gate usd = (repro.usage.usd or 0) + (r.usage.usd or 0), } ``` ```sh timon audit reports/88.md # the report it was built on: does it agree with the audit by hand? timon audit reports/91.md # the next report ``` A workflow worth keeping does a job the project has, on the project's inputs, and its result says what to do next. A script that calls each `flow` pattern on made-up values is a smoke test of the API: it belongs in tests, if anywhere, not in `.timon/`.
Auf GitHub ansehen