Horizon AIOS — Options Packages
An Options Package is an optional, separately-versioned feature you can deploy into a Horizon AIOS install without it being part of the OS core. Options packages are how the AIOS is extended: a package ships its own repository, is cloned into the install, registers itself, and is thereafter kept current, protected, and backed up by the AIOS sync — while the OS core stays lean and neither owns nor ships it.
See also: the install & troubleshooting how-to —
../deployment/aios_options_packages.md.
The first options package is Horizon Lightweight Agentic Project Plans (LAPP)
(horizon_agentic_project_planning), which adds the /project-plan skill. It is used as the worked
example throughout.
First-party options packages
| Package name (registry) | Product name | Adds | Upstream |
|---|---|---|---|
horizon_agentic_project_planning |
Horizon Lightweight Agentic Project Plans (LAPP) | /project-plan skill — six-file living project-plan doc set |
https://github.com/HorizonBrute/HorizonBrute-Horizon_Lightweight_Agentic_Project_Plans_LAPP |
horizon_brain_builder |
Horizon Brain Builder | deploy_brain.py toolchain — builds and deploys brains (sealed RAG runtimes) |
https://github.com/HorizonBrute/Horizon-Brain-Builder |
horizon_aios_dev |
Horizon.AIOS Development | OS dev back-end: documentation/consistency tooling + the write-in seam the OS source is cloned into for modification | https://github.com/HorizonBrute/Horizon.AIOS-Development |
- LAPP is the golden reference implementation. Its installer is the pattern this doc tells package
authors to copy (see "Adopting a generic repo" below), and it is the package the readiness checker
(DOC-0058) cites as the one that scores a clean
READY. - Horizon.AIOS Development is the reflexive case. The other two packages add capability to an
install; this one is the workbench the OS source is cloned into for modification — it is installed
as an options package like any other, and a copy of the OS source is then synced into a directory
inside its own clone for editing. The "modify in OS source, then pull the monorepo as an OS update"
loop becomes, from inside this package, "modify inside the development package's synced source dir,
then pull." It is also the one package whose context block must reach root scope rather than project
scope, since editing the OS happens in
horizon_system/— the strongest concrete case for the root-scope injection target above.
The contract (what makes something an Options Package)
A conforming options package MUST:
- Be standalone. It must work fully on its own, with no dependency on Horizon AIOS. An agent or a
human must be able to use it in a plain repository. (LAPP's
core/is the whole system; the AIOS layer only adds discovery and install.) This keeps packages portable and testable outside the OS. - Ship an installer and uninstaller for AIOS. A cross-platform, standard-library entrypoint
(Python is house style; namespace it
horizon_<package>_*, neverhorizon_aios_*— that prefix is the OS core's) exposing at leastinstall,uninstall,status, andupdate.installdeploys the package's artifacts and registers the package;uninstallreverses the deploy without destroying user data the package created;updaterefreshes the deployment from upstream. - Register itself in the machine-local deployed-packages registry (below) so the AIOS sync can protect, update, and inventory it.
A conforming package SHOULD keep the OS-facing wrapper thin: all real behavior lives in the standalone part, and the AIOS layer only wires discovery, installation, and sync integration.
Anatomy
<package>/
core/ # STANDALONE — the entire feature, zero AIOS dependency
aios/ # OPTIONAL wrapper — thin; discovery + install, no new behavior
install/horizon_<package>_package.py # install / uninstall / update / status
docs/ # package docs (design, decisions/ADRs, examples)
The installer copies whatever the package deploys (a skill, a tool, config) into the appropriate AIOS
location, injects any discovery context, and registers the package. A package's catalog row is
machine-local: it is written to $HORIZON_SKILLS_BIN/index.local.md (untracked) plus the
deployed-packages registry, so the registration survives OS/official-lane updates; the tracked
$HORIZON_SKILLS_BIN/index.md lists core skills only.
Context injection target
The canonical target for a package's discovery-context injection is
$HORIZON_ETC/horizon_aios_options_packages.local.md — never projects/agents.md. It is
@-imported by root agents.md (before the local.* overrides, so a local override still wins),
which reaches every agent at root scope. Reasons this is the target (not projects/agents.md):
- Root-scope reach.
projects/agents.mdonly loads for agents working underprojects/**. An agent working on the OS itself (horizon_system/), in a brain, or anywhere else at root scope never saw a package's context block under the old target. - Survives the official-lane overwrite. The
.local.suffix means the file is git-ignored from OS canon, so the sync's official lane — which overwrites everything exceptprojects/,usrbin/, andbrains/— can never clobber it.projects/agents.mdwas only ever chosen because it happened to sit in that never-overwritten set; the.local.file gets the same protection without being confined to project scope.
Format (normative). Each package injects an idempotent, marker-delimited managed block:
<!-- BEGIN <package-marker> (installed feature — managed block, do not edit by hand) -->
…terse context…
<!-- END <package-marker> -->
installinjects the block only ifBEGIN_MARKERis not already present in the target file (idempotent — safe to re-run).uninstallstrips the exactBEGIN_MARKER … END_MARKERspan.- The marker name is recorded in the registry entry as
payload.context_block_marker, souninstallknows exactly what to strip without guessing. - Reference implementation:
deployed_packages/horizon_agentic_project_planning/aios/install/horizon_project_planning_package.py(CONTEXT_MARKER/BEGIN_MARKER/END_MARKER). - A package installer migrating from the old
projects/agents.mdtarget must first strip any stale same-marker block found there (the registry'spayload.context_block_filerecords the old path when present) before writing to the new target, so an upgraded install never carries the block twice.
The deployed-packages registry
Registry file: $HORIZON_ETC/horizon_deployed_packages.local.json (schema
horizon_deployed_packages/v1). It is machine-local by design — the set of installed packages
differs per machine. The .local.json name means it is git-ignored from OS canon (so an upstream sync
never overwrites it) yet carried by the hourly personal backup sync (its name matches the *local*
re-include rule). Each entry:
| Field | Meaning |
|---|---|
name |
package name |
version |
package version at install |
clone_path |
clone location relative to $HORIZON_ROOT |
upstream |
the canonical upstream URL (where updates come from) |
remotes |
the clone's actual git remotes (captures forks) |
role |
deployment (a mirror) or development-canon (the dev checkout) |
pull_only |
true if the clone's push is disabled (a deployment) |
sync |
if false, the sync neither protects nor updates it |
install_entrypoint |
installer path within the clone; the sync runs <entrypoint> update |
payload |
manifest of what was deployed (for exact uninstall) |
Clone location
A deployed package is a git clone at $HORIZON_SYSTEM/deployed_packages/<name>/ — a dedicated home,
separate from user projects/ and OS tooling usrbin/. It lives under the official-owned
horizon_system/, so it depends on the sync gate (below) to survive upstream syncs.
Sync integration
The two-lane OS sync (horizon_aios_sync.py) treats registered packages three ways:
- Protection gate. The official lane overwrites everything except
projects/usrbin/brainsfrom upstream.official_pathspec()additionally excludes every registered clone withsync != false, so an upstream sync can never clobber a separately-versioned package that lives underhorizon_system/. (horizon_aios_sync.py --statusreports the protected count.) - Update pass. After the official lane,
update_options_packages()invokes each registered package's owninstall_entrypoint update—python <clone>/<install_entrypoint> update. This is how registration is enrollment: a package's installer registers it, and that registration adds it to the update pass with no per-package sync code. Disable withUPDATE_OPTIONS_PACKAGES=no; run in isolation withhorizon_aios_sync.py --update-packages. Best-effort: a failing package is logged and skipped, never failing the OS sync. - Backup. Each clone is a nested git repo, so the nightly nested-repo sync backs it up to its own remote (subject to that clone's push configuration).
Pull-only deployments
A deployment is a read-only mirror: the installer configures its clone pull-only (push URL
pointed at a disabled sentinel), and update is upstream-authoritative (git fetch +
git reset --hard <upstream>, local overwritten). The developer publishes from the development
canon (a separate checkout with push enabled); a deployment only ever receives. The installer
distinguishes the two by clone path (under deployed_packages/ ⇒ deployment). This enforces one-way
flow: canon → upstream → deployment. See the LAPP docs/decisions/ADR-0008.
Lifecycle
- Build — develop the standalone core and the thin AIOS wrapper (installer with
install/uninstall/update/status). Keep the OS namespace clean (
horizon_<package>_*). - Publish — push the development canon to the package's upstream repository.
- Install — on the target AIOS:
git clone <upstream> "$HORIZON_SYSTEM/deployed_packages/<name>" python "$HORIZON_SYSTEM/deployed_packages/<name>/aios/install/horizon_<name>_package.py" installinstalldeploys artifacts, configures the clone pull-only, and registers the package. - Maintain — the AIOS sync's update pass keeps the deployment current automatically; or run
… <entrypoint> update(orhorizon_aios_sync.py --update-packages) on demand. - Uninstall —
… <entrypoint> uninstallreverses the deploy and deregisters the package. It leaves the clone and any user data the package created (self-contained artifacts are never orphaned).
Adopting a generic (non-AIOS) GitHub repo as a package
Most useful repositories were not built with AIOS in mind. To adopt one as an options package you add a thin AIOS wrapper around it — you do not rewrite it:
- Fork or vendor the upstream repo into a repository you control (you need somewhere to add the wrapper and to publish from). The original repo is already "standalone" — the contract's first requirement is satisfied for free.
- Add the AIOS wrapper — an
aios/directory with ahorizon_<name>_package.pyinstaller meeting the contract. Itsinstallmust: - copy/deploy the repo's artifacts into the right AIOS location (a skill →
skills_bin/<name>/; a CLI tool → wherever it belongs; config → its target), and inject any discovery context as a marker-delimited managed block into$HORIZON_ETC/horizon_aios_options_packages.local.md(see "Context injection target" below) — never intoprojects/agents.md, which is OS-owned project scope, not a package injection surface; - register the package in
$HORIZON_ETC/horizon_deployed_packages.local.jsonwithclone_path,upstream,remotes,role/pull_only,sync,install_entrypoint, and apayloadmanifest; - configure a deployment clone pull-only and git-ignore any machine-local
.local.*files it materializes (via the repo's.git/info/exclude, not a tracked.gitignore). Itsuninstallreverses exactly that from thepayload;updatedoes fetch + reset-to-upstream + redeploy;statusprints the registry view. - Keep the fork current with the original using git's normal two-remote model: track the original
as a second remote and merge its releases into your fork, then publish. Your fork is the package's
upstream; deployments pull from your fork. - Publish and install as in the lifecycle above.
The wrapper is small and mechanical; the LAPP installer
(deployed_packages/horizon_agentic_project_planning/aios/install/horizon_project_planning_package.py)
is the reference implementation to copy from.
Worked example — LAPP
- Upstream: the LAPP GitHub repository; deployments clone it to
$HORIZON_SYSTEM/deployed_packages/horizon_agentic_project_planning/. - Installer:
aios/install/horizon_project_planning_package.py(install/uninstall/update/status). - Deploys the
/project-planskill toskills_bin/project-plan/(+ a copy ofcore/askit/), injects a terse context pointer into$HORIZON_ETC/horizon_aios_options_packages.local.md, materializes an admin.local.guide override, and registers itself. - Protected by the sync gate; updated by the sync update pass; pull-only as a deployment.
Requirements checklist
- [ ] Works standalone with no AIOS dependency.
- [ ] Ships
horizon_<name>_package.pywithinstall/uninstall/update/status. - [ ]
installregisters the package (name, version, clone_path, upstream, remotes, role, pull_only, sync, install_entrypoint, payload). - [ ] Deployment clone configured pull-only;
updateis upstream-authoritative. - [ ] Machine-local
.local.*files git-ignored via.git/info/exclude, not tracked.gitignore. - [ ] Namespace is
horizon_<name>_*, neverhorizon_aios_*. - [ ]
uninstallreverses the deploy and deregisters, without destroying user data. - [ ] Discovery context is injected as a marker-delimited managed block into
$HORIZON_ETC/horizon_aios_options_packages.local.md(neverprojects/agents.md); the marker is recorded aspayload.context_block_marker.