Elyra
Elyra The coding agent 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
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

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 curl can 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, 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.