# How we write help articles from tickets we've already solved

Once a week an agent groups our resolved Plain tickets, spots the questions that keep coming back with no help article, drafts the biggest gaps from the real fixes, and opens one PR for a person to review.

Canonical page: https://themachine.myapps.ai/use-cases/ticket-to-kb

Your support team has probably answered the same question five times this
month, and nobody has written it up yet. Someone on the team fixes the issue,
the customer says thanks, and the explanation sinks to the bottom of the inbox.
A few weeks on, a different customer asks the identical thing, it gets fixed
the identical way, and it sinks again. The answer has been worked out a dozen
times. It has never been saved.

So we gave the job to an agent on Machine. Every week it goes through our
resolved Plain tickets, looks for questions that recur but have no help article
behind them, and writes up the most useful of those gaps based on how we
really fixed them. Then it bundles the drafts into a single PR. That's where
most of our new help-center content comes from now: the answers our support
inbox already had.

- **Team:** Machine
- **Runs on:** A weekly schedule that picks up the same session each time
- **Connected systems:** Plain · GitHub (docs/KB repo)
- **Mode:** Drafts only · a single PR each run

## The problem

The answers aren't missing. They're buried in hundreds of closed Plain
conversations, and no one has the hours to dig them out. Picture a support
lead trying to do it by hand. They have to keep track of what the help center
already covers, judge whether a question is a pattern or a one-time oddity, and
then sit down and write the article from a blank page. So the review hardly
ever happens. When it finally does, the pile of undocumented repeat questions
is so big that just choosing a starting point turns into a project.

Handing an AI writing tool a vague brief like "make some help articles" isn't
the answer either. It doesn't know which topics you've already written up, and
it has no idea how your team actually solves the problem. You end up with
duplicates of articles you have, or bland text that doesn't match any real fix.

## What we built

Once a week, a schedule on Machine wakes up the same long-lived session. That
session is a cloud computer (a sandbox) that keeps its own **ledger**, a
running record of past work, so it never starts from zero. On each run it pulls
recently resolved Plain threads and sorts them into recurring topics. It checks
every topic two ways: against the articles already in the KB repo, and against
its own ledger of topics it has drafted before. The winners are questions that
keep arriving in real numbers and still have no article. The agent writes each
one up using the fixes from those actual tickets and opens one PR on the docs
repo. A person has to merge it before anything goes live.

## How it works

### Resume the same session every week

A weekly **cron trigger** doesn't spin up a new computer each time. It picks
up the same long-lived **session**. The first thing that session does is read
its ledger, which lists topics that already have an article, topics sitting in
an open PR waiting for review, and topics it spotted earlier that didn't yet
have enough tickets to be worth a draft. Anything already written or already
in progress gets skipped.

### Teach it how we group and how we write

The agent's playbook lives in **skills** and **memory** and goes wherever it
goes. It spells out how tickets get grouped into a topic, how to tell a genuine
gap from background noise, and the shape we want every article to take. We
also tell it what happened in review: which drafts were merged untouched and
which needed heavy rewriting. Each round of drafts lands a little closer to
ready.

### Hook up Plain and the KB repo

Its access is narrow and deliberate:

- **Plain, for closed tickets.** It can read but not change anything, using an
API key that is stored encrypted and only handed to the agents you grant it
to when a run begins.
- **GitHub, for the docs/KB repo.** It reads the repo to learn what's already
covered. Its only way to write is a pull request from a separate branch,
made with the `gh` CLI and a GitHub token limited to that job.

### Put the limits in place

Publishing is off the table for the agent, and so is merging its own PR. All
it can do is open a pull request from a separate branch. A person reads the
drafts, makes edits, and chooses what reaches the help center. Both the Plain
key and the GitHub token are stored encrypted in the **Secrets Manager** and
only handed to the agents you grant them to, at the start of a run.

### Open one PR full of drafts

From then on, every week produces a single PR holding a handful of draft
articles. Each draft is built from how we really solved the tickets behind it,
and comes with a note for the reviewer on how many tickets asked and what the
topic's history looks like. Our support lead reads it, tidies what needs
tidying, and merges whatever is ready. Merged or not, the ledger moves forward,
so the following week begins with only the topics still missing.

> **The pattern**
> Each week a **cron** wakes the same long-lived **session**, which checks its
> **ledger** of covered topics, groups closed Plain tickets it can only read,
> and opens one GitHub **pull request** of draft articles. It never publishes,
> and it never merges its own work.

## Guardrails

Going from old tickets to drafts carries no risk. Going from a draft to a live
help article is a call the agent never gets to make. Every limit we set sits
on that boundary:

- **Isolation.** Each run picks up inside its own sandbox. The session can
  reach Plain and the KB repo and nothing else, and the pull request is the
  only thing it sends out.
- **Scoped secrets.** The Plain key and the GitHub token are stored encrypted
  in the Secrets Manager and only handed to the agents you grant them to, when
  the run begins.
- **Draft-only.** The agent opens a PR from a separate branch and then it's
  done. The live docs branch is never pushed to, and its own PR is never merged
  by it. A person reviews and publishes.
- **Read-only tickets.** Its Plain access can only read. While it digs through
  tickets for article material, it can't edit, close or answer any of them.
- **Everything is code.** How it groups tickets, how it writes drafts and what
  each system lets it do are files in the repo. They're versioned, and any
  change goes through a reviewed **change request**, not a switch on a settings
  page.

## The outcome

- **Every week:** Fresh closed tickets regrouped against today's list of missing articles
- **Draft-only:** Every article arrives as a PR that a person reviews and merges
- **1 ledger:** A record of covered topics, so nothing gets written twice

All those "we answer this constantly but it's not in the docs" questions now
turn up as one short PR a week, and every draft can be traced to the actual
tickets behind it. The agent's part ends at grouping and drafting. What goes
live on the help center is still the support team's decision.
