Portals are where pull requests come from. Integrations are where they can go: what GitHoot may do with the pull requests it finds, beyond showing them. Today there is one, the Herdr dispatcher. The seam is there so the next one is one new directory, not edits across the app.
Every integration ships inside GitHoot. Integrations in the settings pages lists them, each with an Install button, or Uninstall once it is installed, and each has a page with:
integration.<id>.enabled=on or off to config.txt, the
same one-line edit every other setting gets, and reload the page they were pressed on. Uninstalling
keeps the integration’s settings and files, so installing it again picks up where it left off.on or off, and an unticked switch saves as
off.No restart anywhere: the runner reads config.txt on every pass.
Where a build cannot run an integration, it is still listed, as Not available here, with the reason and no Install button. The list of what exists is the same on every platform.
An integration is code inside the binary, not a program GitHoot starts. That is deliberate. The dispatcher was an external script until 2.4.0, and being external was the problem: Task Scheduler gave it a console window on Windows, the ways around that are what antivirus flags, and bash and PowerShell versions of the same rules drifted. Plugins would bring all of that back for every integration, plus a protocol to keep compatible forever and GitHoot starting programs it did not ship.
Scripts of your own already have a door: the local API serves the same judged lists as JSON.
Everything about one integration lives under src/integration/<id>/. The core (config, page,
serve, main) knows the list in integration::all() and the Integration trait, and nothing about
any one of them.
Info is what the app needs without running it: the id (the config namespace, the URL segment
and the directory name, so lowercase letters only), the name, a one-line summary, the portal kinds
it can act on, its settings, and why this build cannot run it, if it cannot.pass gets the bars, already judged, and acts. It never polls a forge.prepare runs once at startup, installed or not: the dispatcher brings unedited prompts up to
the new defaults there.missing, problems, page and action feed its page. problems is what is wrong
with its settings on this machine before any pull request arrives; the runner logs it too.integration::fake is a test double that imports nothing from herdr. It compiles only if the
trait can be implemented without a single Herdr type, which is the whole promise of the seam.~/.githoot/integrations/<id>/, and its own settings, integration.<id>.*.One thread runs every installed integration, off the poll thread, so a slow git fetch never holds up
the icon. It wakes right after each poll publishes, and at least every thirty seconds, which is also
how soon an Install takes effect.
What a pass did is logged at info level; what went wrong with a pull request at error level, every time; what is wrong with the setup, such as a missing tool or a folder that is not there, at error level once until it changes.