PIPELINE. Observe -> plan -> run the plan's steps through the port, round after round, until the planner says healthy or gives up.
The loop owns no decision. Which remedy, and when to stop escalating, is
hive-gimp.lifecycle.model/plan; this namespace only carries the history
forward and records every round, so the report says exactly what was seen,
what was decided and what each step answered.
A system is {:probe ILinkProbe :lifecycle ILifecycle :clock IClock :policy Policy}. Adding a step kind is a defmethod run-step here plus its
enum member in model/StepKind (OCP).
PIPELINE. Observe -> plan -> run the plan's steps through the port, round
after round, until the planner says healthy or gives up.
The loop owns no decision. Which remedy, and when to stop escalating, is
`hive-gimp.lifecycle.model/plan`; this namespace only carries the history
forward and records every round, so the report says exactly what was seen,
what was decided and what each step answered.
A `system` is `{:probe ILinkProbe :lifecycle ILifecycle :clock IClock
:policy Policy}`. Adding a step kind is a `defmethod run-step` here plus its
enum member in `model/StepKind` (OCP).BOUNDARY. The lifecycle port against the real host: a TCP probe, the MCP
plug-in's own check_server/restart_server, the session D-Bus, ss, and
a headless flatpak GIMP this adapter launches and owns.
The no-human channel into a RUNNING GUI GIMP is GIMP's own D-Bus service.
GIMP 3 owns org.gimp.GIMP.UI on the session bus and exports
BatchRun(interpreter, command), the method gimp -b uses to hand a batch
to an instance that is already open. Running relaunch-source through
python-fu-eval there starts plug-in-mcp-server inside that GIMP. The
call is queued by GIMP and answers (true,) at once; the server binds a
moment later, which is why every remedy is followed by :await-answer.
Nothing has to be installed in GIMP for this, and GIMP need not restart.
Pure helpers (relaunch-source, launch-argv, listener-pids,
bus-answer-true?) are public so the suite pins them without a host.
BOUNDARY. The lifecycle port against the real host: a TCP probe, the MCP plug-in's own `check_server`/`restart_server`, the session D-Bus, `ss`, and a headless flatpak GIMP this adapter launches and owns. The no-human channel into a RUNNING GUI GIMP is GIMP's own D-Bus service. GIMP 3 owns `org.gimp.GIMP.UI` on the session bus and exports `BatchRun(interpreter, command)`, the method `gimp -b` uses to hand a batch to an instance that is already open. Running `relaunch-source` through `python-fu-eval` there starts `plug-in-mcp-server` inside that GIMP. The call is queued by GIMP and answers `(true,)` at once; the server binds a moment later, which is why every remedy is followed by `:await-answer`. Nothing has to be installed in GIMP for this, and GIMP need not restart. Pure helpers (`relaunch-source`, `launch-argv`, `listener-pids`, `bus-answer-true?`) are public so the suite pins them without a host.
DOMAIN. The link to GIMP's MCP server as a value, and the decision of what to do about it as a value.
Nothing here touches a socket, a process or a bus. An adapter OBSERVES the
world into an Observation; this namespace CLASSIFIES it into a
LinkState and PLANS a HealPlan; the executor in
hive-gimp.lifecycle.heal runs the plan's steps through the port. The
decision is therefore testable with nothing but maps.
Two open sets, both extended by registration (OCP):
classify rules are data in classification, checked in order; a new
state is a new row.
plan-for a multimethod on the state; a new state's remedy is a
defmethod, never an edit to a case.
Escalation is a function of HISTORY, the step kinds already run in this
heal. A remedy that was tried and did not take is not tried again the same
way: restart-server before kill-listener, one relaunch before giving
up. That is what keeps heal! bounded without a retry counter per state.
DOMAIN. The link to GIMP's MCP server as a value, and the decision of what
to do about it as a value.
Nothing here touches a socket, a process or a bus. An adapter OBSERVES the
world into an `Observation`; this namespace CLASSIFIES it into a
`LinkState` and PLANS a `HealPlan`; the executor in
`hive-gimp.lifecycle.heal` runs the plan's steps through the port. The
decision is therefore testable with nothing but maps.
Two open sets, both extended by registration (OCP):
classify rules are data in `classification`, checked in order; a new
state is a new row.
plan-for a multimethod on the state; a new state's remedy is a
`defmethod`, never an edit to a `case`.
Escalation is a function of HISTORY, the step kinds already run in this
heal. A remedy that was tried and did not take is not tried again the same
way: `restart-server` before `kill-listener`, one relaunch before giving
up. That is what keeps `heal!` bounded without a retry counter per state.The lifecycle seams. Three small protocols, split by reason to change (ISP):
ILinkProbe look, never touch: one Observation of the link.
ILifecycle the remedies a HealPlan names, one method per step kind
that acts on the world.
IClock time, so awaiting an answer is testable without sleeping.
The host adapters live in hive-gimp.lifecycle.host; the suite's fakes in
test/hive_gimp/lifecycle/fake.clj. Every method returns data and never
throws for an expected failure: a remedy that did not work is a
{:ok? false} the planner escalates on, not an exception that ends the
heal before the next remedy is tried.
The lifecycle seams. Three small protocols, split by reason to change (ISP):
ILinkProbe look, never touch: one `Observation` of the link.
ILifecycle the remedies a `HealPlan` names, one method per step kind
that acts on the world.
IClock time, so awaiting an answer is testable without sleeping.
The host adapters live in `hive-gimp.lifecycle.host`; the suite's fakes in
`test/hive_gimp/lifecycle/fake.clj`. Every method returns data and never
throws for an expected failure: a remedy that did not work is a
`{:ok? false}` the planner escalates on, not an exception that ends the
heal before the next remedy is tried.FACADE. The MCP face of the lifecycle: gimp_lifecycle with
action = status | heal.
A separate tool rather than a gimp command because it must work exactly
when the gimp commands cannot: with the plug-in dead there is nothing on
the other end of the command socket to answer them.
FACADE. The MCP face of the lifecycle: `gimp_lifecycle` with `action` = status | heal. A separate tool rather than a `gimp` command because it must work exactly when the `gimp` commands cannot: with the plug-in dead there is nothing on the other end of the command socket to answer them.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |