Delivering work
A finished run leaves a summary comment on the issue. That says what happened; it does not say where anything is. Artifacts are the other half: the pull request, the branch, the document, the preview link.
What Félagi knows about pull requests
Nothing, deliberately.
There is no GitHub integration, no GitLab integration, and no plan for one. An issue in Félagi can be about code, a document or an analysis, and a run can deliver any of those. Building one host's model of "a pull request" into the core would make the other cases second-class, and would put a vendor's API on the critical path of a product whose whole argument is that it stays out of the way.
Instead the agent opens the pull request itself, with gh or glab — tools
that are already on the runtime and already authenticated — and reports the URL
back. Félagi stores a link and a state. That is the entire integration.
How an agent reports a delivery
Every run gets an environment variable:
FELAGI_DELIVERY=/…/workdir/.felagi/delivery.json
The agent writes JSON there. The daemon reads it after the run and sends it with the completion.
{
"artifacts": [
{
"type": "pull_request",
"url": "https://github.com/acme/api/pull/482",
"title": "Add a rate limiter to the daemon routes",
"state": "open"
},
{ "type": "branch", "reference": "felagi/ACM-231-rate-limiter" }
]
}
A bare array works too. An agent following a written instruction gets the wrapper wrong about half the time, and refusing a delivery over that would lose the one thing the run was for.
The types
| Type | Needs | Notes |
|---|---|---|
pull_request |
url |
The only type that moves the issue — see below |
branch |
reference |
A name, not an address. Push it, or it is not a delivery |
commit |
reference |
A revision |
document |
url |
Something written |
url |
url |
A preview, a dashboard, a report |
state is optional on any of them: draft, open, merged, closed.
Why a file and not the CLI's output
Every provider formats its output differently. A delivery has to survive Claude Code, Codex, Elyra and whatever comes next without an adapter change for each one, and a file is the only thing all of them can write.
It is cleared before every run, so a resumed task cannot report yesterday's link
as today's. It lives outside any checkout, so it never appears in git status
and never ends up in somebody's commit. And custom_env cannot redirect it: a
task that could set that path could report a link to anywhere as this issue's
outcome.
The skill
None of the above happens on its own. The agent has to be told, and that is what a skill is for. Create one in Administration → Skills, and attach it to any agent that works on code.
---
name: Deliver as a pull request
description: Commit, push and open a pull request when the work is done.
---
# Deliver as a pull request
When you have finished the work on an issue, do not stop at describing it.
Hand it over the way a colleague would.
## Branch
Work on a branch named after the issue, never on the default branch:
```
felagi/<ISSUE-IDENTIFIER>-<short-description>
```
For example: `felagi/ACM-231-rate-limiter`.
## Commit
Write a message that says what changed and why. Reference the issue
identifier in the body, not the subject.
## Push and open the pull request
```bash
git push -u origin HEAD
gh pr create --fill --body "Resolves ACM-231"
```
Use `glab mr create` if the remote is GitLab. If neither tool is
authenticated, push the branch anyway and report it as a branch artifact —
a pushed branch somebody can open themselves is still a delivery. A branch
that exists only on the runtime is not.
## Report it
Write the file named by the `FELAGI_DELIVERY` environment variable:
```json
{
"artifacts": [
{
"type": "pull_request",
"url": "<the URL gh printed>",
"title": "<the pull request title>",
"state": "open"
}
]
}
```
Do this last, after the pull request exists. Reporting a URL for something
that has not been created yet is worse than reporting nothing.
It needs gh on the runtime, authenticated
Same rule as git credentials: Félagi holds no token for anybody's forge. Before
attaching this skill, check that a person can run gh auth status on that
machine as the user the daemon runs as.
What happens on the issue
The artifacts appear as chips above the run output, beside the agent's summary comment — a link where a colleague would have put one.
A pull request moves the issue to In review. This is the one place an agent changes an issue's status, and it is narrow on purpose:
- Only for a
pull_request. A summary is not a delivery, and a branch or a link is not something a person has been asked to review. - Only from a status that comes before review. An agent must not reopen work somebody has already closed.
- It is written to the timeline, attributed to the agent, so an issue that moved on its own says who moved it.
That gives the human-in-the-loop gate for free: the agent delivers, a person reviews and merges.
Closing the loop
Nothing polls anybody's API, so Félagi does not find out on its own that a pull request was merged. A forge can tell it:
POST /hooks/deliveries/<FELAGI_DELIVERY_SECRET>
{ "url": "https://github.com/acme/api/pull/482", "state": "merged" }
| State | What happens |
|---|---|
merged |
The issue moves from In review to Done |
closed |
The issue moves back to In progress — the work came back |
| anything else | The artifact's state is updated, the issue is not touched |
Set FELAGI_DELIVERY_SECRET to switch it on. Until then every request is a
404 — and a wrong token gets the same 404 as a missing one, because a 401 tells
somebody they have found the right URL.
Three things it deliberately does not do:
- It only closes a handover. An issue that is in progress, on hold, or already finished by a person is left alone. A stale webhook must not overwrite somebody's work.
- It does not credit the agent. A person merged that pull request. An audit
log that names the wrong actor is worse than a quiet one, so the entry has no
actor and says
via: delivery— the timeline reads "The delivery status changed to done". - It knows nothing about GitHub. A URL and a state, that is all. Any forge,
any CI server, or a person with
curlcan post it.
On GitHub, one workflow is enough:
on:
pull_request:
types: [closed]
jobs:
tell-felagi:
runs-on: ubuntu-latest
steps:
- run: |
curl -sS -X POST "$FELAGI_HOOK" \
-H 'Content-Type: application/json' \
-d '{"url":"${{ github.event.pull_request.html_url }}","state":"${{ github.event.pull_request.merged && 'merged' || 'closed' }}"}'
env:
FELAGI_HOOK: ${{ secrets.FELAGI_DELIVERY_HOOK }}
Not there yet
- Nothing polls. If nobody posts the hook,
statestays as the agent left it. Félagi will not go and look. - No diff in Félagi. Review happens where the code lives. A patch viewer is a code review tool, and there are good ones already.
- No automatic merge, and none planned as a default. The review is the value of the model.