Architecture Evolution#
The HISPEC control architecture has been rebuilt twice since the repository was created in October 2024, each time moving closer to the COO command-and-control model. Reading the tree today, that history is visible: directories from earlier eras sit alongside current ones, and it is not obvious from the filenames which are live.
This page records what changed and why, which parts of the tree are dead, and where the next iteration goes. Knowing why a design was left behind is what stops it being re-proposed — and the reasons are what justify the shape of the architecture described in Architecture Overview.
timeline
title HISPEC ICS architecture eras
section KROOT / KTL
Oct 2024 : Keck KROOT tree imported
: XML keyword definitions, C/Python dispatchers
: Qt GUI scaffolding, init.d service scripts
section Driver library
Apr–Aug 2025 : Vendor drivers as COO-Utilities submodules
: pip-installable src/ layout
: Daemons hoisted to daemons/
section Standalone daemons
Nov–Dec 2025 : First per-mechanism daemons
: HispecDaemon base over libby transport
: YAML config ingestion
section libby keywords
2026 : Keyword registry replaces service dicts
: HispecDaemon collapses onto LibbyDaemon
: peer_id / group_id naming, systemd deployment
section Next — additive
Planned : Command traceability, daemon state, fault handling
: Simulation backends
: Procedures, algorithms, orchestration, GUIs
Era 1 — KROOT / KTL (Oct 2024 – mid 2025)#
HISPEC started as a Keck KROOT tree, the standard Keck instrument software
build. Control interfaces were declared as XML keyword definitions and .defs
files, compiled by KROOT Makefiles into dispatchers, with service directories
per subsystem and init.d scripts to start them.
The surviving artefacts are daemons/hsowenv (1-Wire environment),
daemons/hsdewar (Lakeshore dewar control), daemons/hsssd, plus Makefile,
Mk.instrument, init.d/ and qt/. daemons/hspower was one of them until
its KROOT scaffolding was removed and the directory reused for the libby PDU
daemon.
Why it moved on. KROOT ties development to a summit-like environment: the
build needs /kroot and Keck-internal modules (DFW, SerialStream) that do
not exist on a developer laptop or a bare Ubuntu box. For an instrument being
built at Caltech and Palomar, years before summit integration, that made the
normal development loop — write code, run it against a controller on the bench —
unreasonably expensive. The .sin template and Makefile layer also meant that
adding a mechanism was a build-system change rather than a configuration change.
Era 2 — the driver library (Apr – Aug 2025)#
Before the control architecture was settled, the hardware layer was. Through 2025, low-level drivers were written for each controller — Sunpower, PI, Newport, Xeryon, Inficon, Lakeshore, Standa, Thorlabs — and pulled out into their own repositories under the COO-Utilities organisation, vendored back as git submodules.
Two restructures in this period (#6, #14) moved the repository to a src/
layout so it became a normal pip-installable Python package, and #73 in August
2025 hoisted the service directories to a top-level daemons/.
This era’s output is the part of the architecture that has survived both
rewrites unchanged. Drivers are the stable layer precisely because they know
nothing about the control architecture above them: the same
hispec.driver.thorlabs.fw102c served the KTL dispatcher and serves the libby
daemon.
In February 2026 the package namespace was renamed hispec.util →
hispec.driver (#127), making the layer’s role explicit.
Era 3 — standalone daemons (Nov – Dec 2025)#
The first daemons that owned their own hardware and process appeared in
November 2025: calyjrack, then atcpress. In December, the pattern was
generalised — #113 added a HispecDaemon base class (a ~220-line class over
libby.Libby, handling signals, lifecycle and a services dict of RPC
handlers), and #115 added YAML config ingestion.
This is the era that set the durable shape of the system: one executable per
mechanism type, -c config.yaml, a base class that owns the transport, and
configuration rather than code as the way to add a mechanism.
Why it moved on. The services dict was a bag of named RPC handlers with no
type information, no units, no declared access mode and no discoverability — a
client had to read the daemon source to know what it served. That is
serviceable for one daemon and unworkable for twenty-two instances across two
subsystems.
Era 4 — libby keywords (2026 – present)#
The current architecture. Three changes, in order:
Keywords replace service dicts (April 2026, #135, first in the PI daemon).
libby’s KeywordRegistry gave typed keywords with getters, setters,
validators, units, descriptions and timeouts, plus auto-generated
keys.list / keys.describe. A daemon became self-describing, and the
instrument regained the Keck keyword vocabulary on a new transport.
HispecDaemon collapses onto LibbyDaemon (July–August 2026, #152). The
local base class shrank from ~220 lines to four: everything it did was now in
libby, shared with other COO instruments, and all HISPEC needed to say was
“RabbitMQ, discovery off”.
Naming, aggregation and deployment are standardised (September 2026). The
-d/daemon-id flag was dropped in favour of peer_id + group_id in config
(#174); peer IDs were renamed to lowercase module acronyms matching the Jira
mechanism names (#186); multi-stage daemons gained consolidated whole-mechanism
keywords (#189, #205); a shutdown keyword was added so daemons can be stopped
over the same interface as everything else (#195); and systemd template units
made deployment reproducible (#198).
The through-line of all three is the same: make the interface uniform enough that a client can be written against the instrument rather than against a particular daemon.
What is legacy#
These directories are from Era 1, are not built, and are not run:
Path |
Was |
Status |
|---|---|---|
|
KTL 1-Wire environment service |
Dead. Last touched Aug 2025 as part of a directory move. |
|
KTL Lakeshore dewar dispatcher |
Dead. Superseded by |
|
KTL spectrograph service |
Dead. |
|
KROOT build |
Dead. Still references KPF in a comment. |
|
KTL service startup |
Dead. Superseded by |
|
KTL-era Qt GUI scaffolding |
Dead. Untouched since Oct 2024. Not a starting point for a libby GUI. |
They have been left in place rather than deleted because the XML keyword
definitions still document the intended keyword sets for subsystems whose libby
daemons have not been written — hsssd in particular describes the
spectrograph. Treat them as design reference, not as code.
daemons/hspower is the one that has been through this: its KTL power service
— Makefile, Mk.service, the .conf.in configs and the .xml.in keyword
templates — is gone, and the directory now holds the libby pdu daemon,
pdu_capabilities.py and pdu_models/ instead. The old templates are still in
git history if the strip- and outlet-level keyword names are ever wanted.
Two other stale references worth knowing about: pyproject.toml still ignores
the Keck-internal DFW and SerialStream modules for pylint’s benefit, and the
root README.md described the layout as “each mKTL service has a directory” —
mKTL was evaluated and is not what HISPEC uses.
In progress#
Not yet on main, but shaping the next change:
Ansible provisioning (
pgupta/playbook) — turning the host build notes into a playbook, so instrument hosts are provisioned reproducibly rather than by following an RST document by hand.Fiber switcher driver (
pgupta/add_fiber_drivers) — thehispec-fiber-switchersubmodule is vendored but has no daemon yet.
Era 5 — the direction#
The next iteration is already scoped, and unlike the previous two it is additive rather than a redesign. The keyword contract, the transport and the daemon layer stay as they are; what gets built sits above them.
In order: make the control layer observable (command traceability, daemon state, fault handling, structured errors), then make it developable without hardware (simulation), then build the layers the COO model puts on top — procedures, algorithms, orchestration and GUIs.
The sequencing is deliberate. Each of those first items changes the contract that a GUI or sequencer would be written against, so they are cheaper to adopt before those clients exist than to retrofit around them. Conformance and Roadmap holds the detail and the priority order; Architecture Overview sketches the target shape.
That the next era is additive is the return on the two rewrites. Era 1 ended because the build system dictated where you could develop; Era 3 ended because untyped service dicts could not scale past a handful of daemons. Both were structural limits that no amount of incremental work could relieve. The current architecture has no equivalent limit in view — the remaining work is a list of things to add, not a reason to start again.