Harmonic Chart harmonic-n@1 — Specification (W-05)
Status: Implemented v1 convention · Task: W-05 · Updated: 2026-10-25 · Implementation: W-10 package.
A harmonic chart multiplies every ecliptic longitude by an integer factor. It is the smallest transformation in the engine and the easiest one to mislabel: the output is derived coordinates, not astronomy, and the transformed chart does not inherit the natal house ring.
1. Admitted variant
| Field | Value |
|---|---|
| Technique | harmonics |
| Version | 1 |
| Variant | harmonic-n |
| Rulebook entry | { "technique": "harmonics", "version": "1", "variant": "harmonic-n", "hash": "<payload hash>" } |
Rule payload (validated by validateHarmonicRules):
{
"transform": "multiply",
"angles": "transformed",
"houses": "none",
"minFactor": 2,
"maxFactor": 64,
"allowedFactors": null
}
factor is a request parameter, constrained by the payload. allowedFactors: [4, 5, 7, 9]
pins a reviewed subset; null admits every integer in [minFactor, maxFactor]. The factor is part
of the result and of the rule evidence, never of the technique identity.
2. Inputs
| Input | Source | Meaning |
|---|---|---|
| Reference snapshot | Engine | Bodies, angles, speeds |
factor |
Request | Integer harmonic number |
| Profile | Engine | Body set, aspect/orb policy |
Injected GeometryPort |
Optional | Harmonic-chart contacts |
No ComposerPort and no ProbePort: the transformation is pure and needs no new astronomy.
3. Transformation
λ_h = wrap360(n × λ_0)
| Point | Rule |
|---|---|
| Bodies | Every body in the reference snapshot, profile order |
| Ascendant | wrap360(n × asc) when angles: "transformed" and the angle exists |
| Midheaven | wrap360(n × mc) when angles: "transformed" and the angle exists |
| Other angles | Not transformed |
| Cusps | Not transformed and never attached |
wrap360 is defined in the composite specification §3.1. The
multiplication is done in full precision and wrapped once, at the end. Wrapping an earlier
intermediate value is equivalent modulo 360, but it hides the single normalisation point and makes
fixture diffs harder to read.
Every transformed point is:
{ "kind": "derived", "method": "harmonics.transform", "version": "1",
"sources": ["<reference snapshot hash>"] }
The factor is retained in the technique result and in the deciding RuleMatch.facts
({ factor: n }), because DerivedPointOrigin has no factor field. Two results with different
factors are never merged or cached as one.
4. Speeds
The transformation has an exact derivative:
s_h = (s_0 === null) ? null : n × s_0
This is the derivative of the transformed coordinate, not a claim about the body’s physical motion,
and it is the only reason harmonic contacts can carry a phase. Unknown source speed stays null and
reaches the shared geometry as phase: 'unknown'; zero is never substituted.
Contacts are evaluated with motion: "returned-speeds" so both derived derivatives are used. If the
source chart was composed without speeds, every harmonic phase is unknown and the contacts are
still reported.
5. Angles and houses
| Policy | Behaviour |
|---|---|
angles: "transformed" |
Ascendant and midheaven are multiplied like bodies and labelled derived |
angles: "omitted" |
Not admitted by this variant; a different version |
houses: "none" |
The harmonic chart has no cusps; overlays are unavailable |
Natal cusps are never attached to the harmonic chart. A harmonic chart is not a relocated natal
chart, and a transformed point does not occupy a natal house. A request for harmonic house placement
is LAYER_UNSUPPORTED; an overlay evaluated against the natal chart is a separate, explicit
consumer choice and is not produced here.
6. Contacts (optional)
policy = { ...profile.aspectPolicy, motion: 'returned-speeds' }
contacts = geometry.evaluateChartAspects(harmonicGeometry, policy)
- Same-chart contacts of the harmonic chart, not a re-labelling of natal aspects.
- Orb set and aspect ids are the profile’s; the harmonic technique adds no orb table.
harmonicGeometrycarriespointsfrom §3, derived origins, derived speeds and no cusps.- With geometry absent,
contactsis omitted withmissing-aspects; the harmonic chart is still a successful result.
7. Result
| Field | Meaning |
|---|---|
method |
{ id: 'harmonic-n', version: '1', transform: 'multiply' } |
factor |
The requested integer |
reference |
{ snapshot: <hash>, instant } |
points[] |
{ id, longitude, speedLongitude, origin: DerivedPointOrigin } |
angles |
{ ascendant, midheaven } or null |
houses |
Always null |
contacts? |
Harmonic-chart contacts (§6) |
missing |
missing-angles, missing-aspects |
The original reference snapshot is returned unchanged and is never mutated. Repeated calls with the same snapshot and factor are byte-identical.
8. Composer facts and ports
| Need | Provided by | Absent behaviour |
|---|---|---|
| Reference bodies and speeds | Reference snapshot | INSUFFICIENT_INPUT |
| Reference angles | Reference snapshot (house read) | missing-angles; bodies still transform |
| Aspect evaluation | GeometryPort |
missing-aspects only |
ComposerPort, ProbePort |
Not used | — |
Declared requirements: snapshot: 'houses', upstream: ['positions', 'houses'],
optionalUpstream: ['geometry'], dependencies: [].
The admitted variant transforms the reference angles, so the snapshot requirement is houses. A
host without a house observer fails context assembly explicitly; a positions-only harmonic variant
is a different version. The missing-angles row in §9 applies to snapshots supplied directly to the
pure transformation.
9. Failures
| Case | Behaviour |
|---|---|
factor absent or non-integer |
INVALID_REQUEST |
factor === 1 |
INVALID_REQUEST; the identity transform is not a harmonic |
factor < minFactor (other than 1), > maxFactor, or outside allowedFactors |
LIMIT_EXCEEDED |
Unknown transform, angles or houses value |
INVALID_RULES |
| Reference has no angles | missing-angles; bodies still transform |
| Harmonic houses requested | LAYER_UNSUPPORTED |
| Geometry absent | contacts omitted, missing-aspects |
| Source speed unknown | speedLongitude: null, phase: 'unknown'; never zero |
10. Worked examples
n is the factor, λ the reference longitude.
| Id | Input | Expected |
|---|---|---|
| H-F1 | n=4, λ=10° |
40° |
| H-F2 | n=4, λ=100° |
40° (400 mod 360) |
| H-F3 | n=4, λ=350° |
320° (1400 mod 360) |
| H-F4 | n=4, λ=90° |
0° (360 mod 360) |
| H-F5 | n=5, λ=137.4° |
327° (687 mod 360) |
| H-F6 | n=7, λ=300° |
300° — a fixed point, because 6 × 300 = 5 × 360 |
| H-F7 | n=2, λ=200° |
40° |
| H-F8 | n=4, s=0.5°/day |
s_h = 2°/day |
| H-F9 | n=4, s=null |
null, phase: 'unknown' |
| H-F10 | n=4, asc 15°, mc 105° |
asc 60°, mc 60° (420 mod 360) |
| H-F11 | n=4, derived Sun 40°, derived Moon 40° |
conjunction, separation 0, phase: 'exact' |
| H-F12 | n=1 |
INVALID_REQUEST |
| H-F13 | n=4.5 |
INVALID_REQUEST |
| H-F14 | n=65 |
LIMIT_EXCEEDED |
| H-F15 | allowedFactors: [4,5,7,9], n=6 |
LIMIT_EXCEEDED |
| H-F16 | House placement requested | LAYER_UNSUPPORTED |
| H-F17 | Same snapshot and n=4 requested twice |
identical points, speeds and contacts |
H-F6 is the arithmetic check that catches a missing wrap: 7 × 300 = 2100, and 2100 mod 360 is
300, so the transformed point must equal the source. H-F2, H-F3 and H-F4 catch per-step wrapping
and off-by-one sign errors.
11. Unsupported variants
| Variant | Why unsupported |
|---|---|
| Non-integer factors | The transformation is undefined on the circle |
n = 1 |
Identity; not a harmonic chart |
n > 64 |
Declared bound; the result is still defined but unreviewed |
angles: "omitted" |
A different point set; needs its own fixtures |
| Transformed or inherited houses | No reviewed construction; natal cusps are never attached |
| Harmonic-of-harmonic chains | Composition order and identity are undefined here |
| Harmonic midpoint trees, dial charts, 90° dials | Different techniques |
| Interpretation of harmonic aspects | Downstream products |
12. Prerequisites before W-10
The implementation and coordinator integration now satisfy these runtime seams; convention sources remain the responsibility of each supplied production rulebook. See the package tests.
| Id | Prerequisite | Owner |
|---|---|---|
| P1 | A reviewed source for the multiply-transform, the factor bounds and the angle policy | Harmonic reviewer |
| P2 | harmonics.harmonic-n@1 payload schema and validator registered |
W-10 |
| P3 | Shared geometry accepts derived points with defined derivatives and no cusps (C-03) | Compose owner |
| P4 | Determinism fixture: two calls over one immutable snapshot | W-10 |