Confluence
Diese Seite gibt es nur auf Englisch — sie gehört zu den Betriebsanleitungen, die wir bewusst einsprachig halten.
A practical runbook for the target system Confluence
(github.com/benjaminLedel/covey-plugin-pack/confluence). The unit is the
page; the plugin covers Confluence Cloud and Server/Data Center.
Short version: an API token (Cloud) or a personal access token (Data Center), stored as
confluence_token+confluence_url. The agent reads pages as Markdown and writes them as Markdown — the storage format Confluence keeps them in never reaches it. There is no intake: Confluence wakes nobody. These are configuration steps, not a rebuild.
What this is for, and what it is not
Confluence is the documentation the other systems hang off. The specification a Jira ticket links to, the runbook a merge request invalidates, the release note somebody looks for next quarter. An agent reads it while working on something else, and writes to it when that work is done.
Two boundaries are worth drawing before the first token is created:
- It is not a source of work. Nobody is assigned a page. There is no
HEARTBEAT.mdentry for this system and nonur-wenn: confluence— the plugin has no work check at all, and a test in the pack asserts that it never grows one by accident. Give the agent Jira or GitLab beside it, or it has no occasion to look here. - It is not covey's wiki memory. covey has one of its own
(
../spec/05-memory.md): linked Markdown pages with apgvectorindex, private to the agent, curated for retrieval. That is the agent's memory. Confluence is the company's documentation — shared with humans, with its own permissions and its own history. An agent uses the first to remember and the second to be understood.
Cloud or Data Center
One plugin, two deployments, and they are further apart than Jira's:
| Confluence Cloud | Server / Data Center | |
|---|---|---|
confluence_token |
mail@example.com:<API token> |
<personal access token> |
| HTTP auth | Basic | Bearer |
| Base path | https://acme.atlassian.net/wiki |
https://confluence.acme.example |
| Pages | REST v2 (/api/v2/pages) |
REST v1 (/rest/api/content) |
| Search | REST v1 (/rest/api/content/search) — CQL never moved to v2 |
REST v1 |
| A space is named by | a numeric id (v2) | its key |
A pair with a colon is Cloud, a single value is a personal access token. The
/wiki context path exists only in the Cloud, and the plugin appends it when it
is missing — the browser hides it, so nobody has it in hand. Where the inference
is wrong, write it out:
confluence_url = https://confluence.acme.example auth=bearer api=1
1. Step-by-step instructions
1.1 In Confluence: an account and a token
Give the agent a user of its own (covey-bot) with access to the spaces it
is to work. Every page version and every comment carries that name, and a reader
of the page history should be able to tell an agent's edit from a colleague's.
Cloud: log in as that user → id.atlassian.com → Security → API tokens.
It is the same kind of token Jira uses — one Atlassian account, two
products, two secrets in covey. If the agent already works Jira, this is the
same account and can be the same token value.
Server/Data Center: as that user → Profile → Personal Access Tokens.
1.2 In covey: deposit the secrets
| Secret | Value |
|---|---|
confluence_url |
https://acme.atlassian.net/wiki (or without /wiki — it is appended) |
confluence_token |
covey-bot@acme.example:<API token> / <PAT> |
Optional components after the URL, separated by spaces:
confluence_url = https://acme.atlassian.net/wiki space="ENG" api=2 auth=basic
space= is a boundary, not a default. An agent whose credential names a
space reads and writes that space and no other — through search, through
get_page with an id somebody quoted at it, and through everything that writes.
Several: space="ENG,OPS".
Note what it costs, because it differs from Jira's project wall: a Jira key
carries its project in front of the hyphen, so ACME-17 can be judged without
asking anybody. A Confluence page id carries nothing at all, so the space is
read before the page is touched — free on a read (the page is fetched anyway),
one call on a write. A wiki is exactly the system where somebody wants that
assurance in writing, so it is worth the call.
The wall is applied to a search by bracketing the agent's own query:
your query: type = page AND title ~ "runbook" ORDER BY created
what is sent: space in (ENG) AND (type = page AND title ~ "runbook") ORDER BY created
1.3 In covey: enable the target system
- system: confluence scope: read,write,comment
| Scope | What it permits |
|---|---|
read |
search, get_page, list_children, list_spaces, list_comments, list_attachments, download_attachment |
comment |
comment |
write |
append_to_page, update_page, create_page, add_labels, attach_file |
Read-only is a real option here. An agent that pulls the specification into
its context and writes nothing needs scope: read, and that is a defensible
setup for the first weeks. The prompt documentation is narrowed to the scopes
granted, so a read-only agent does not carry the writing procedure through every
turn.
1.4 Testing
The connection test on the plugin page names the account, the deployment and the wall:
covey Bot (covey-bot@acme.example) · Cloud · ENG
Read all three. The deployment is an inference from the token's shape, and this is the one place somebody can see that it is wrong.
2. Appending versus replacing
This is the section to read before granting write.
append_to_page {"page_id":"131075","version":7,"message":"release 1.2",
"body":"## Release 1.2\n\nThe importer guards the null case now — ACME-17."}
append_to_page is the one an agent normally wants. Almost everything an
agent writes to a wiki is an addition — a release note, a finding, a line in a
runbook. Appending cannot lose what somebody else wrote, and the existing body
is written back untranslated: rendering it to Markdown and back would
reformat everything a human wrote, and a diff in which the whole page moved is a
diff nobody reviews.
update_page replaces the entire body. It is the right action when the page
really is the agent's to rewrite, and the wrong one otherwise. The two are
separate guard-rail subjects (confluence:append_to_page and
confluence:update_page) precisely so that an organisation can permit the first
and hold the second for approval:
| Rule | Effect |
|---|---|
confluence:update_page → ask |
every full rewrite goes through the Approvals page |
confluence:update_page → deny |
the agent can only ever add |
The version number
Confluence numbers every revision and refuses a write that is not exactly one ahead. That sounds like protection and is not: a plugin that reads the current number and increments it will happily overwrite an edit made in between.
So the agent passes the version it read — the number get_page returned:
page stands at 7, agent read 7 → write goes through, page is now 8
page stands at 8, agent read 7 → refused: "somebody wrote in between"
Without it the last write wins, and the result says so. That is not hidden because a page nobody else touches is the common case, and demanding a version there would cost a call and teach the agent a step it forgets.
message becomes the version comment in the page history. Somebody will read it
before they read the diff.
3. What the agent sees instead of storage format
A page is stored as an XHTML derivative with Atlassian's own elements woven
through it — <ac:structured-macro> for a code block, <ac:link> around an
<ri:page> for a link to another page, <ac:image> around an <ri:attachment>
for a picture. The plugin renders it to Markdown on the way in and builds it
back on the way out.
What survives the round trip: headings, paragraphs, bullet and numbered lists,
task lists (Confluence's real checkboxes, from - [x]), fenced code blocks
with their language, block quotes, tables, links, inline code, bold, italics.
What does not: a table comes back as Markdown-ish rows and goes back as a
paragraph, panels become [warning] …, and a macro whose output only the server
knows — a page tree, an included Jira filter — becomes [pagetree macro] rather
than a silent gap. The sentence is what matters; a formatting that was not
recognised is a smaller loss than a page that does not get written.
The agent writes Markdown. It should never be asked to produce storage format itself: the result is almost-XHTML, a 400 it cannot learn from, and a retry with a slightly different tree.
4. The three systems, one loop
Jira ACME-17 "Importer drops rows"
│ links → Confluence "Import pipeline" (the spec)
▼
Confluence get_page {"title":"Import pipeline"} ← read BEFORE coding
│
GitLab checkout · branch ACME-17-… · commit "ACME-17 …" · MR
│
Confluence append_to_page (release note, runbook correction)
│
Jira comment (the MR link) · transition
Two habits worth putting into the agent's PLAYBOOKS.md:
- Read the linked page before writing code. The ticket's summary is a
headline; the requirement is on the page.
get_pagetakes a title, which is what a Confluence link shows. - Write down what you had to find out. The runbook that was wrong, the parameter nobody had documented — that is a section, appended, with the issue key in the text so the two can be found from each other.
5. Attachments
list_attachments {"page_id":"131075"}
download_attachment {"page_id":"131075","name":"architecture.png"}
Addressed by name, not by id: an attachment id is not something an agent
has, while the name stands in the page it just read ([attachment: architecture.png]). A name that is not there is answered with the ones that
are.
The way back is attach_file; note that the upload alone puts nothing in the
page — a append_to_page that mentions the file is what makes anybody find it.
6. Env reference (Confluence-relevant)
| Variable | Default | Effect |
|---|---|---|
COVEY_CONFLUENCE_INTAKE_SPACES |
(empty) | allowlist of space keys the plugin may write to. Empty = every space. Installation-wide; the per-agent wall is space= in confluence_url. |
COVEY_CONFLUENCE_ATTACHMENT_MAX_MB |
25 |
per file, in both directions (1…1024). |
There is no COVEY_CONFLUENCE_WEBHOOK_SECRET, because there is no webhook —
Confluence Cloud has none an admin can simply enter; that needs a Connect/Forge
app.
7. Troubleshooting
| Symptom | Cause | Remedy |
|---|---|---|
HTTP 404 on every call |
the /wiki path missing or doubled, or a Cloud site addressed as v1 |
give the site URL; the plugin appends /wiki itself for a Cloud credential |
HTTP 401 right at the connection test |
Cloud token used without the mail address, or a PAT sent as Basic | mail:token for Cloud, the bare token for Data Center; auth= writes it out |
page … lies outside your spaces |
the per-agent wall | intended — widen space= only if the agent really is to work there |
Version must be incremented / 409 |
two writes raced | the agent should pass the version from get_page; then it gets the readable error instead |
A page comes back with [pagetree macro] in it |
a macro the server renders | expected — the content is not in the page, it is generated on view |
| Markdown appears literally in the page | the body was sent to a system that is not this plugin | the plugin always translates; check that the action really was confluence/* |
| The agent never opens Confluence | no occasion | it is not a source of work — the playbook has to say when to look |
See also
ops-jira.md— the ticket, and the same Atlassian accountops-gitlab.md,ops-github.md— the code../spec/05-memory.md— covey's own wiki memory, and why it is a different thing