Western dependency adapters
Status: Implemented seams · Updated: 2026-10-25
The Western packages are built ahead of a frozen Compose implementation by narrowing every upstream contact to a Western-owned shape. An adapter copies values into that shape; technique code never imports a Composer, Binder or provider implementation, and no upstream fact is recalculated while copying.
Two seam kinds exist, deliberately separate:
- Port binder and shared conversions live in
packages/western, because every technique needs the same gate inspection and the same snapshot-to-geometry conversion. - Technique result narrowing lives in the consuming package under
src/adapters/, because only that technique knows which facts its rules read.
Implemented seams
| Seam | Location | Upstream input | Western output |
|---|---|---|---|
| Convention → chart request | packages/western/src/profiles/context.ts |
Admitted WesternProfile plus per-request instant and observers |
Shared ChartContextInput for an injected ComposerPort |
| Capabilities → gate status | packages/western/src/upstream/binder.ts |
ComposerPort.capabilities() and its ports |
WesternUpstreamStatus, require(gate) and connect(gates) |
| Snapshot → geometry chart | packages/western/src/upstream/adapters.ts |
ChartSnapshot |
Shared GeometryChart with observed point origins, angles and cusps |
| Snapshot → natal facts | packages/western/src/calculations/natal/adapters/facts.ts |
ChartSnapshot and Composer-evaluated ChartContact[] |
NatalChartFacts, NatalAspect[] |
| Technique resources → admitted rules | packages/western/src/rulebooks/registry.ts |
Manifest plus supplied technique payloads | Frozen AdmittedRules<T> with technique/version/variant, hash and rulebook identity |
toChartContextInput is the outbound seam: it emits exactly the shared request shape, so swapping the
Composer implementation changes nothing in Western code. It refuses observers that contradict the
profile’s pinned position origin and never selects an ayanamsa, house system or frame at request time.
With { houses: false } it composes positions only, so a technique that needs no cusps does not
require a house observer.
bindWesternUpstream inspects Composer once per engine call. A capability the host has not connected
stays closed and names its owning task, and connect hands a feature only the ports it declared;
a technique therefore cannot fabricate an astronomy or geometry answer while an upstream task is
pending. geometryChartFrom copies bodies, angles and cusps into the shared GeometryChart and keeps
observed origins and evidence, so a derived point can never be mistaken for a provider position.
toNatalFacts is the inbound seam: position, resolved sign, degree in sign, house placement, motion,
cusps and aspect annotations are copied, with null meaning unavailable. Because rules consume
NatalChartFacts, a fixture snapshot, a real Composer snapshot and a future reader all feed the same
rule code. evaluateNatal(facts, aspects, rules) exposes the adapted entry point directly.
getWesternTechniqueRules is the admission seam: the registry verifies the manifest and content
hashes, then passes the stored payload through the owning technique’s validator. A resource is never
cast to a schema it does not satisfy, and the returned AdmittedRules<T> carries the identity the
engine compares with the registered feature.
Planned seams
| Seam | Consumer | Intended narrowing |
|---|---|---|
| Cross-chart geometry | synastry |
CrossChartContact/HouseOverlay → source/target contacts with directional overlays; the port is still injected, only its result shape is narrowed |
| Bounded probes | transit, returns |
ProbeResult/ProbeBudget → technique-owned solver steps, retaining residuals and evaluation counts as evidence |
| Composer charts | progressions, returns, composite |
ComposerPort.compose → technique-specific mapped contexts; derived charts are labelled separately from observed ones |
Rules for a new adapter
- Put shared port inspection and chart-shape conversion in
packages/western; put technique-specific result narrowing in that technique’ssrc/adapters/. - Import Compose and Binder types only. A runtime import means the technique is no longer pure.
- Copy; never re-derive placement, sign resolution, aspect separation or rounding.
- Keep upstream nulls null and unknown motion unknown; do not substitute zero.
- Validate the copied shape and fail with a shared
WesternErrorcode rather than returning a partial object. - Test the adapter against a synthetic fixture in the same shape the real upstream publishes, so the technique and engine tests stay runnable without a provider or ephemeris file.
Remaining external dependencies
- S-01 workspace registration.
packages/western/src/*is registered inpnpm-workspace.yamlwithbuild:western,check:westernandtest:westernroot scripts. The integration agent owns these files; keep the entries when merging other family work. - Contract freeze on
packages/chart-composer. Composer contracts now belong to the consolidated chart-composer package. The Western adapters pin their current shapes; a later contract change is absorbed in the seams listed above, not in technique or rule code. - S-02 integration.
@astrology/westerncan already be constructed with anyComposerPort; its tests use a synthetic host. The remaining work is a host that wires the real Binder/Composer into both engines and exposes their results.