The org model landed last session — units, memberships, relationships, cycle detection, a compositional DSL, YAML surface, and desiredstate integration. What it didn’t have was annotation-driven declaration, the same zero-code path that @Identity and @Disposition give agent descriptors.

That’s what this session adds: @OrgUnit, @OrgMembers, and @Supervises.

@OrgUnit(kind = "rig")
@OrgMembers({
    @OrgMemberDef(agentId = "witness-1", role = "witness"),
    @OrgMemberDef(agentId = "polecat-1", role = "worker")
})
@Supervises(source = "witness-1", target = "polecat-1")
@Supervises(source = "witness-1", target = "polecat-2")
public interface RigAlpha {}

The unit ID derives from the class name (kebab-case, same as @Identity). @Supervises is @Repeatable for the common case. @OrgRelationships with @OrgRelationshipDef covers everything else — BACKS_UP, ESCALATES_TO, DELEGATES_TO, and EXTENDED.

The build extension follows the same Quarkus pattern: Jandex scan at build time, config extraction into a recordable data class, synthetic OrgRegistrar CDI beans produced via a @Recorder. Nine deployment tests cover the full surface — explicit IDs, auto-derived IDs, members, supervision, general relationships, hierarchy via parentUnit, and tenancy.

The Extension Chain Problem

I expected this to be mechanical — copy the eidos-annotations pattern, swap types, done. One thing bit: the Quarkus extension descriptor plugin enforces that if a deployment module depends on another extension’s runtime artifact, the corresponding runtime module must also declare that dependency. The processor needed NameDerivation from casehub-eidos-annotations, and pulling that in would have dragged the entire eidos extension chain into org-annotations.

The fix was simpler than the problem: duplicate the 42-line utility. Two copies of a pure function beats one tangled dependency graph.

The Missing Bootstrap

Building the annotations surfaced a gap in the previous session’s work. The org model had OrgRegistrar as its declarative SPI and ClasspathYamlOrgRegistrar implementing it — but nothing consumed the registrars. No startup observer collected OrgRegistrar beans and fed their definitions into OrgRegistry. The equivalent of AgentDescriptorBootstrap simply didn’t exist for org.

OrgBootstrap is 15 lines. It observes StartupEvent, iterates Instance<OrgRegistrar>, and registers units and relationships. Without it, neither YAML nor annotation-declared orgs would actually register at runtime. The kind of gap you only find when you wire the consumer.


<
Previous Post
From Wanting to Doing
>
Next Post
The Goal That Earns Its Rank