Factory Equipment Services
choco intermediate 4 min read
ELI5
There are four small machines in the factory, each doing one thing to a document on its way out the door. One sorts (winnower), one syncs metadata across vaults (conch), one fills in the body content (mould), and one wraps it up with verified links (wrapper).
Technical Deep Dive
Each lives at services/the-<name>/ and follows the same shape: Dockerfile, pyproject.toml, src/, README declaring persona + ports + subject prefix. They share the cocoa-butter runtime for NATS and OTel.
Pipeline Order
flowchart LR in[document in] --> winn[the-winnower<br/>Nut-Sorting Room<br/>refinement] winn -->|EntityExtracted| mould[the-mould<br/>Inventing Room<br/>hydration] mould -->|BlockRendered| wrap[the-wrapper<br/>Press Room<br/>resolution] wrap -->|TagResolved| out[document out] conch[the-conch<br/>Fudge Room<br/>coordination] -.cross-vault sync.-> mould conch -.cross-vault sync.-> wrapService Responsibilities
| Service | Persona | Responsibility | Subject prefix |
|---|---|---|---|
| the-winnower | Nut-Sorting Room | quality-floor detection, entity extraction; emits SlopDetected, EntityExtracted, DocumentWinnowed | choco.events.refinement.> |
| the-conch | Fudge Room | cross-platform property sync with CRDT conflict resolution; emits PropertySyncStarted, ConflictDetected, ConflictResolved | choco.events.coordination.> |
| the-mould | Inventing Room | MDX skeleton hydration, metadata injection; emits HydrationStarted, BlockRendered, HydrationCompleted | choco.events.hydration.> |
| the-wrapper | Press Room | tag parsing, resolver dispatch, cross-repo link verification; emits TagsParsed, ResolverDispatched, TagResolved, CrossRepoLinkVerified | choco.events.resolution.> |
Port Convention
Each service exposes HTTP / gRPC / metrics on a contiguous port triple:
| Service | HTTP | gRPC | Metrics |
|---|---|---|---|
| the-winnower | 8086 | 7076 | 9101 |
| the-conch | 8087 | 7077 | 9102 |
| the-mould | 8088 | 7078 | 9103 |
| the-wrapper | 8089 | 7079 | 9104 |
Persona Tagging
Every service sets factory.room as a structured-log field at bootstrap. Operators reading aggregated logs filter by factory.room = "Press Room" rather than matching on service name — the persona is the contract surface for ops dashboards.
Key Terms
- persona / Room → branded codename used in log fields and human-facing dashboards; the package name is technical, the persona is operational.
- slop → low-quality or AI-noise content; the-winnower scores documents against the slop rubric.
- resolver dispatch → the-wrapper’s tag-parsing pipeline routes resolution queries to per-tag-type resolvers.
Q&A
Q: Why is the-conch in Fudge Room rather than the main pipeline?
A: It synchronises across vaults rather than processing one document end-to-end (services/the-conch/README.md:9); the metaphor “listens across vaults and keeps them in phase” maps to a side-channel role.
Q: What links the four services besides naming?
A: Shared runtime (cocoa-butter), the structured-log field convention (factory.room), and the same port triple pattern. They do not call each other directly — coordination is via NATS subjects.
Q: Where is the slop rubric defined?
A: In the-winnower service code (not in this repo’s contracts/); the README only says “scores them against the slop rubric” (the-winnower/README.md:9). Inferred: the rubric is internal to the service.
Examples
A new “Tasting Room” service for sentiment analysis would: pick the next contiguous port triple (8090/7080/9105), import cocoa-butter, set factory.room = "Tasting Room" at bootstrap, claim a new domain prefix (choco.events.tasting.> plus a new proto/events/tasting/v1/ directory), and publish a stream-and-ordering comment on each new event proto.
neighbors on the map
- Choco Factory Architecture Map onboarding to a new choco service repo
- NATS Subject Taxonomy wiring a new consumer to the right stream