Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Starf An activity monitor for Apple silicon that never invents a number e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Start here
Concepts
Release notes
What's new
Elyra
Delivering work

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

As little as it can, and one host by name.

An issue here can be about code, a document or an analysis, and a run can deliver any of those. So the core stores a link and a state, and an agent opens the pull request itself with gh or glab — tools already on the runtime and already authenticated — then reports the URL back. That is still how a run delivers, and it works with any host.

This page used to say there was no GitHub integration and no plan for one. That was wrong for a reason worth writing down: it protected the model and ignored the people. An agent reported its deliveries because Félagi had asked it to. A person opening the identical pull request reported nothing, because nobody had told them to — and no amount of neutrality closes that gap. A product whose claim is that people and agents are one workforce cannot show one of them working and not the other.

So there is now a GitHub connection: Administration → GitHub. GitHub posts what happened, Félagi finds the issue named in the branch, and the pull request appears on it whoever opened it. A branch cut for an issue starts that issue, which is the earliest honest signal there is that somebody has begun.

See GitHub for the whole of it. Listening is all of it unless you ask for more. Reporting back is a separate step and optional: install a GitHub App with two permissions and Félagi adds a check to each pull request naming its issue, and keeps one comment current. The check is deliberately neutral — Félagi is reporting, not judging, and a red cross would block a merge over a link.

The generic route is unchanged and unprivileged — hooks/deliveries still takes a URL and a state from any forge at all.


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, 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 curl can post it. The GitHub-specific hook above did not replace this one: that one speaks a single host's vocabulary so nobody has to translate, and this one asks the sender to translate so any host can be heard.

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 }}

Did it do what it said?

A run ends with a summary, and it ends with a pull request. Félagi now holds one against the other.

The summary and the diff disagree The summary says it added tests. No test file was changed.

This is not a review of the code. It has no opinion on whether the change is good. It asks one question — was this claim kept? — and only about claims a list of file paths can settle.

The difference is rarely a lie. It is a model summarising optimistically, which is exactly the discrepancy a person spends ten minutes finding by hand, on every delivery, forever.

What it checks

The summary says It looks for
it added tests any test file, in any language's convention
it changed documentation any .md, .rst or file under docs/
it added a migration any file under migrations/
nothing needed changing whether anything changed anyway

That last one is worth the most. An optimistic summary is a nuisance; an account that is the wrong way round means nobody reviews a delivery they were told was empty.

What it refuses to do

A false alarm costs far more than a missed one. The first time this flags a run that was fine, somebody stops reading it, and then it may as well not exist. So:

  • "Fixed the failing test" is not a claim to have written one. The verb has to be one of writing
  • A test is recognised by any ecosystem's convention — tests/, spec/, _test.go, .test.tsx, _spec.rb. A rule that only knew Laravel would report a false discrepancy on every Go repository
  • If the file list cannot be fetched, nothing is reported. An empty list means either an empty pull request or a failed call, and from here those are the same thing
  • A delivery from a person is not checked. No run, no summary, no claim

Checking happens when GitHub tells Félagi about the pull request, and the result is stored — the page never waits on somebody else's API to draw. A delivery that was checked and had nothing to report is recorded as such, so "checked, fine" and "never checked" are not the same silence.

Not there yet

  • Nothing polls. If nobody posts the hook, state stays 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.