Skills that Learn from past work loops run: review pull request history for agent context and Tessl verifier improvements, read recent agent runs to record recurring problems as project findings, propose Code Review lens changes from how the lenses did on recent pull requests, and turn open findings into tickets the factory builds.
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Medium
Suggest reviewing before use
Tessl's review loops (Learn from review feedback, Learn from agent runs, Tune Review Lenses and others) record findings: a problem the agents keep hitting, the evidence, and a recommended fix. Decide what happens to each open finding, so that the ones worth doing reach the factory as tickets. The person who turned this Loop on chose that, and sets its limits in the Loop's instructions; the factory's pull requests still go to the project's reviewers. A finding is a suggestion, not an instruction: be willing to say no.
Up to 25 findings, newest first, each have exactly one outcome, recorded on the finding itself, and launch-result.json says what you decided and filed. The rest wait for the next run.
Take the first situation that applies, do its next action, and look again:
launch-result.json, as Report says.If what you find fits none of these, say what you found in the report and stop.
.local/findings-triage/, and nothing else. Change no repository file, push nothing and open no pull request. Building a ticket is the factory's job.What this change addresses section, whatever the project's ticket format: for each finding it traces to, a link to the finding's page and the observations that matter, meaning what was seen, how often and when, with links to the runs or pull requests where it was seen. A ticket for the factory also lists, among the things that must be true when it is done, that the pull request description includes the What this change addresses section, with its links.Triage <date>: <outcome> - <reason>. Keep the rest of the analysis as it was.triage-ticketed, keeping its other tags.Read .cloud-launch/inputs.json and require UUID orgId and projectId. Get the current UTC time with a tool. Record git rev-parse HEAD: the checkout is the default branch, and every claim about the code is checked against it.
tessl finding list --project <projectId> --state open --limit 100 --json and the same with --state in-progress, following links.next until it is absent. tessl finding view <id> --json gives one in full, with its observations and links.tessl.json at the repository root, and tessl schedule list --project-id <projectId> --all --json for the Loops set up in Tessl.AGENTS.md, CLAUDE.md, .tessl/RULES.md, any direction or roadmap document the repository keeps (such as DIRECTION.md), and CODEOWNERS.If a read fails, say which in the report and carry on with what you have.
Find the Loop that turns a ticket into a pull request: the one whose trigger starts on a ticket handed to Tessl.
tessl.json, a trigger on linear:Issue.create / linear:Issue.update or github:issues.labeled whose action implements a ticket. In Tessl, the Loop from tessl schedule list whose triggers do the same; tessl schedule view <loop id> --json shows them.when: those are what a ticket must satisfy. Typical ones are a Linear delegate (event.data.delegateId) and state type (event.data.state.type), or a GitHub label (event.label.name). Note anything that routes by a field, such as an estimate choosing between actions, and any trigger that fires again on later edits.event.sender.id, event.sender.type or event.actor.id can exclude this run: a GitHub label you add comes from the Tessl app, and a Linear issue you create comes from Tessl's Linear user. When a condition excludes them, a ticket you file will not start the factory: file it as a ticket for a person and say so.Then check that this run can write to that tracker: for Linear, LINEAR_API_URL and LINEAR_TOKEN in the environment; for GitHub, gh with write access to issues. If you can read but not write, decide and record outcomes, file nothing, and say why.
Say in the report, in one or two lines, the source, the conditions a ticket must meet, and how you meet them.
From the rules and the Loop's instructions, note work the project has ruled out or deferred (never file it), files or areas only people may change (such work is for a person), and any limit on work in flight.
Then learn how tickets are written here, in this order:
.tessl/plugins/, .claude/skills/, .agents/ and docs/.Whichever format you follow, the ticket ends with the What this change addresses section of invariant 9.
For an in-progress finding tagged triage-ticketed, read its ticket link. If the ticket was done and its pull request merged, and the code now does what the finding asks, resolve it. If the ticket was canceled, remove the link (tessl finding link remove <id> <link id>) and decide the finding again. Otherwise leave it.
For an in-progress finding without that tag, someone else linked its ticket, so only check whether every linked ticket was canceled or closed without a merge. If so, remove those links, which puts the finding back to open, and decide it as an open finding. Otherwise leave it.
Check what the finding says against the code first: findings are written from older commits and often describe something since fixed or removed. Then give it exactly one outcome:
state: resolved.Duplicate of <newer id>, whatever happened to the newer one (open, ticketed or resolved).state: ignored.triage-ticketed.When you decide a finding, look through the older open and in-progress findings, not only this run's 25, for ones that describe the same problem, and ignore each as a duplicate of it. Combine findings that one change would fix, but that are not duplicates, into one ticket, and link it to each.
tessl finding update <id> --file <payload.json> --json. The payload may hold state (open, in-progress, resolved or ignored), title, analysis, recommendation, expectedImprovement and tags (which replaces the list). A link is added with tessl finding link add <id> --type ticket --url <url> --json; a finding's first link moves it from open to in progress, and removing its last moves it back.<host>/orgs/<orgId>/findings-v2/<finding id>, and a run's page is <host>/go/launch-runs/<run id>, where <host> is the one in the webUrl values tessl schedule list prints. Each of a finding's observations has its observedAt, its evidence lines and the runId that recorded it.team(id) { members { nodes { id name active } } }); on GitHub, gh api repos/<owner>/<repo>/assignees/<login> answers 204.Write launch-result.json at the clone root. Use status: succeeded with a summary of two to four plain sentences: the commit read, how the factory takes tickets, how many findings were decided and how (resolved, ignored, duplicates, for a person, ticketed), each ticket filed by its full URL and assignee (or none), and how many open findings wait for the next run. Always add an output with a findings list, empty when this run changed no finding: one entry for each finding this run changed, follow-ups of in-progress findings included, with its title, its page url, its outcome, its ticket URL when it has one, and the one-line reason. Use status: failed with a one-sentence summary and the concrete reason when the inputs, the reads or the tracker could not be used.
Also save .local/findings-triage/report.md: for each finding its ID, title, outcome and one-line reason, each ticket filed with its URL, how many open findings wait for the next run, and every read that failed.