caelus 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,167 @@
1
+ /**
2
+ * astroengine interpretation matching + resolver -- the plug for a content layer.
3
+ *
4
+ * The {@link interpretationContext} projection turns a chart into ranked fact
5
+ * atoms; this layer lets a developer plug in *meaning*. A {@link Selector}
6
+ * matches atoms (and reports which ones, so a claim carries its provenance); a
7
+ * {@link Rule} pairs a selector with text; an {@link InterpretationSource}
8
+ * bundles rules (a tradition, a house style, a third-party corpus). The engine
9
+ * ships the contract and the resolver, never the content: {@link interpret}
10
+ * runs sources against a context and returns a ranked {@link Reading}, each
11
+ * entry tagged with the atom ids it rests on.
12
+ *
13
+ * Selectors read the projection directly, so they express the whole fact model
14
+ * -- house, dignity, pattern membership, signature dominance, aspect phase and
15
+ * strength -- which the geometric, time-only `query` predicates cannot.
16
+ */
17
+ import type { FactAtom, InterpretationContext } from "./interpretation.js";
18
+ /** The result of a {@link Selector}: did it match, and on which atoms. */
19
+ export interface Match {
20
+ matched: boolean;
21
+ /** The atoms that satisfied the selector (the provenance). Empty for a
22
+ * satisfied absence test ({@link matchNone}). */
23
+ atoms: FactAtom[];
24
+ }
25
+ /** Tests a whole {@link InterpretationContext} and reports the matching atoms. */
26
+ export type Selector = (ctx: InterpretationContext) => Match;
27
+ /** Matches placement atoms by any subset of body / sign / house / retrograde /
28
+ * a held dignity. */
29
+ export declare function hasPlacement(filter?: {
30
+ body?: string;
31
+ sign?: string;
32
+ house?: number;
33
+ retrograde?: boolean;
34
+ dignity?: string;
35
+ }): Selector;
36
+ /** Matches aspect atoms. `between` is an unordered pair; `minStrength` filters
37
+ * loose aspects; `phase` filters applying/separating. */
38
+ export declare function hasAspect(filter?: {
39
+ a?: string;
40
+ b?: string;
41
+ between?: [string, string];
42
+ aspect?: string;
43
+ phase?: string;
44
+ minStrength?: number;
45
+ }): Selector;
46
+ /** Matches configuration atoms by kind and/or a participating body. */
47
+ export declare function hasPattern(filter?: {
48
+ kind?: string;
49
+ body?: string;
50
+ }): Selector;
51
+ /** Matches a structural-signature facet, e.g. `("element", "fire")`. */
52
+ export declare function hasSignature(facet: string, value?: string): Selector;
53
+ /** Matches an angle atom by which angle and/or its sign. */
54
+ export declare function hasAngle(angle: string, sign?: string): Selector;
55
+ /** Matches dispositor atoms by body, its dispositor, and/or the final flag
56
+ * (a body in its own domicile that terminates a dispositor chain). */
57
+ export declare function hasDispositor(filter?: {
58
+ body?: string;
59
+ dispositor?: string;
60
+ final?: boolean;
61
+ }): Selector;
62
+ /** Matches a mutual reception, optionally involving a given body. */
63
+ export declare function hasReception(filter?: {
64
+ body?: string;
65
+ }): Selector;
66
+ /** Matches a fixed-star conjunction by the catalog star and/or the body on it. */
67
+ export declare function hasStar(filter?: {
68
+ body?: string;
69
+ star?: string;
70
+ }): Selector;
71
+ /** Matches a Hermetic lot by name, and/or its sign or house. */
72
+ export declare function hasLot(filter?: {
73
+ lot?: string;
74
+ sign?: string;
75
+ house?: number;
76
+ }): Selector;
77
+ /** Matches only when every selector matches; returns the union of their atoms. */
78
+ export declare function matchAll(...sels: Selector[]): Selector;
79
+ /** Matches when any selector matches; returns the atoms from those that did. */
80
+ export declare function matchAny(...sels: Selector[]): Selector;
81
+ /** Matches when the selector does NOT match (an absence test); no atoms. */
82
+ export declare function matchNone(sel: Selector): Selector;
83
+ /** One interpretation: a condition and the text it licenses. */
84
+ export interface Rule {
85
+ /** Stable id, unique within its source. */
86
+ id: string;
87
+ /** The condition over the fact projection. */
88
+ when: Selector;
89
+ /** The interpretation text, or a function of the match for templating. */
90
+ text: string | ((match: Match, ctx: InterpretationContext) => string);
91
+ /** Multiplies the matched atoms' salience when ranking (default 1). */
92
+ weight?: number;
93
+ /** Free-form labels (theme, polarity, ...) carried through to the entry. */
94
+ tags?: string[];
95
+ }
96
+ /** A pluggable corpus of rules: a tradition, a house style, a third party. */
97
+ export interface InterpretationSource {
98
+ id: string;
99
+ version: string;
100
+ rules: Rule[];
101
+ }
102
+ /** One licensed statement in a {@link Reading}, with its provenance. */
103
+ export interface ReadingEntry {
104
+ /** `"<source>/<rule>"`. */
105
+ id: string;
106
+ source: string;
107
+ rule: string;
108
+ text: string;
109
+ /** Ids of the fact atoms this entry rests on -- the audit trail. */
110
+ atomIds: string[];
111
+ /** Sum of the matched atoms' salience times the rule weight. */
112
+ salience: number;
113
+ tags?: string[];
114
+ }
115
+ /** A resolved interpretation: ranked entries, each citing its facts. */
116
+ export interface Reading {
117
+ jdUt: number;
118
+ entries: ReadingEntry[];
119
+ }
120
+ /**
121
+ * Run interpretation sources against a fact projection and return a ranked
122
+ * {@link Reading}. Each rule whose selector matches emits an entry carrying the
123
+ * matched atom ids (provenance) and a salience = sum of those atoms' salience x
124
+ * the rule weight. The engine never ships the content: the sources are the
125
+ * caller's.
126
+ *
127
+ * @param ctx A projection from {@link interpretationContext}.
128
+ * @param sources One or more {@link InterpretationSource} corpora.
129
+ * @returns The {@link Reading}; entries are sorted by descending salience.
130
+ */
131
+ export declare function interpret(ctx: InterpretationContext, sources: InterpretationSource[]): Reading;
132
+ /** Entries about the same facts, gathered. */
133
+ export interface ReadingGroup {
134
+ /** Union of the group's cited atom ids -- the facts it is about. */
135
+ atomIds: string[];
136
+ /** Member entries, highest salience first. */
137
+ entries: ReadingEntry[];
138
+ /** Distinct tags across the members. */
139
+ tags: string[];
140
+ /** True when a declared conflicting tag-pair both appear (the corpus made
141
+ * opposing claims about the same facts). */
142
+ contested: boolean;
143
+ /** The group's salience (its strongest entry). */
144
+ salience: number;
145
+ }
146
+ export interface ReconcileOptions {
147
+ /** Tag pairs that contradict, e.g. `[["affirming", "challenging"]]`. */
148
+ conflicts?: [string, string][];
149
+ /** Drop an entry whose `text` duplicates a higher-salience one. */
150
+ dedupe?: boolean;
151
+ }
152
+ /**
153
+ * Group a {@link Reading}'s entries by the facts they share, so statements about
154
+ * the same atoms surface together rather than scattered through a flat list --
155
+ * the substrate for "everything said about this placement" and for spotting
156
+ * contention. Entries are connected when their cited atoms overlap; an entry
157
+ * citing nothing (an absence rule) stands alone. A group is `contested` when a
158
+ * declared conflicting tag-pair both appear in it.
159
+ *
160
+ * Semantic contradiction is the corpus author's to declare (via `tags` +
161
+ * `conflicts`); the resolver does the bookkeeping, not the judgement.
162
+ *
163
+ * @param reading A reading from {@link interpret}.
164
+ * @param opts Conflicting tag pairs and optional text de-duplication.
165
+ * @returns Groups sorted by descending salience.
166
+ */
167
+ export declare function reconcile(reading: Reading, opts?: ReconcileOptions): ReadingGroup[];
@@ -0,0 +1,192 @@
1
+ const hit = (atoms) => ({ matched: atoms.length > 0, atoms });
2
+ // ------------------------------------------------------------- atom selectors
3
+ /** Matches placement atoms by any subset of body / sign / house / retrograde /
4
+ * a held dignity. */
5
+ export function hasPlacement(filter = {}) {
6
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "placement"
7
+ && (filter.body === undefined || a.body === filter.body)
8
+ && (filter.sign === undefined || a.sign === filter.sign)
9
+ && (filter.house === undefined || a.house === filter.house)
10
+ && (filter.retrograde === undefined || a.retrograde === filter.retrograde)
11
+ && (filter.dignity === undefined || a.dignities.includes(filter.dignity))));
12
+ }
13
+ /** Matches aspect atoms. `between` is an unordered pair; `minStrength` filters
14
+ * loose aspects; `phase` filters applying/separating. */
15
+ export function hasAspect(filter = {}) {
16
+ const pair = filter.between ? [...filter.between].sort() : null;
17
+ return (ctx) => hit(ctx.atoms.filter((at) => {
18
+ if (at.kind !== "aspect")
19
+ return false;
20
+ if (filter.a !== undefined && at.a !== filter.a)
21
+ return false;
22
+ if (filter.b !== undefined && at.b !== filter.b)
23
+ return false;
24
+ if (pair && [at.a, at.b].sort().join() !== pair.join())
25
+ return false;
26
+ if (filter.aspect !== undefined && at.aspect !== filter.aspect)
27
+ return false;
28
+ if (filter.phase !== undefined && at.phase !== filter.phase)
29
+ return false;
30
+ if (filter.minStrength !== undefined && at.strength < filter.minStrength)
31
+ return false;
32
+ return true;
33
+ }));
34
+ }
35
+ /** Matches configuration atoms by kind and/or a participating body. */
36
+ export function hasPattern(filter = {}) {
37
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "pattern"
38
+ && (filter.kind === undefined || a.pattern === filter.kind)
39
+ && (filter.body === undefined || a.bodies.includes(filter.body))));
40
+ }
41
+ /** Matches a structural-signature facet, e.g. `("element", "fire")`. */
42
+ export function hasSignature(facet, value) {
43
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "signature" && a.facet === facet
44
+ && (value === undefined || a.value === value)));
45
+ }
46
+ /** Matches an angle atom by which angle and/or its sign. */
47
+ export function hasAngle(angle, sign) {
48
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "angle" && a.angle === angle
49
+ && (sign === undefined || a.sign === sign)));
50
+ }
51
+ /** Matches dispositor atoms by body, its dispositor, and/or the final flag
52
+ * (a body in its own domicile that terminates a dispositor chain). */
53
+ export function hasDispositor(filter = {}) {
54
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "dispositor"
55
+ && (filter.body === undefined || a.body === filter.body)
56
+ && (filter.dispositor === undefined || a.dispositor === filter.dispositor)
57
+ && (filter.final === undefined || a.final === filter.final)));
58
+ }
59
+ /** Matches a mutual reception, optionally involving a given body. */
60
+ export function hasReception(filter = {}) {
61
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "reception"
62
+ && (filter.body === undefined || a.bodies.includes(filter.body))));
63
+ }
64
+ /** Matches a fixed-star conjunction by the catalog star and/or the body on it. */
65
+ export function hasStar(filter = {}) {
66
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "star"
67
+ && (filter.body === undefined || a.body === filter.body)
68
+ && (filter.star === undefined || a.star === filter.star)));
69
+ }
70
+ /** Matches a Hermetic lot by name, and/or its sign or house. */
71
+ export function hasLot(filter = {}) {
72
+ return (ctx) => hit(ctx.atoms.filter((a) => a.kind === "lot"
73
+ && (filter.lot === undefined || a.lot === filter.lot)
74
+ && (filter.sign === undefined || a.sign === filter.sign)
75
+ && (filter.house === undefined || a.house === filter.house)));
76
+ }
77
+ // ----------------------------------------------------------------- combinators
78
+ /** Matches only when every selector matches; returns the union of their atoms. */
79
+ export function matchAll(...sels) {
80
+ return (ctx) => {
81
+ const parts = sels.map((s) => s(ctx));
82
+ return parts.every((p) => p.matched)
83
+ ? { matched: true, atoms: dedupe(parts.flatMap((p) => p.atoms)) }
84
+ : { matched: false, atoms: [] };
85
+ };
86
+ }
87
+ /** Matches when any selector matches; returns the atoms from those that did. */
88
+ export function matchAny(...sels) {
89
+ return (ctx) => {
90
+ const matched = sels.map((s) => s(ctx)).filter((p) => p.matched);
91
+ return matched.length
92
+ ? { matched: true, atoms: dedupe(matched.flatMap((p) => p.atoms)) }
93
+ : { matched: false, atoms: [] };
94
+ };
95
+ }
96
+ /** Matches when the selector does NOT match (an absence test); no atoms. */
97
+ export function matchNone(sel) {
98
+ return (ctx) => ({ matched: !sel(ctx).matched, atoms: [] });
99
+ }
100
+ function dedupe(atoms) {
101
+ const seen = new Set();
102
+ return atoms.filter((a) => (seen.has(a.id) ? false : seen.add(a.id)));
103
+ }
104
+ /**
105
+ * Run interpretation sources against a fact projection and return a ranked
106
+ * {@link Reading}. Each rule whose selector matches emits an entry carrying the
107
+ * matched atom ids (provenance) and a salience = sum of those atoms' salience x
108
+ * the rule weight. The engine never ships the content: the sources are the
109
+ * caller's.
110
+ *
111
+ * @param ctx A projection from {@link interpretationContext}.
112
+ * @param sources One or more {@link InterpretationSource} corpora.
113
+ * @returns The {@link Reading}; entries are sorted by descending salience.
114
+ */
115
+ export function interpret(ctx, sources) {
116
+ const entries = [];
117
+ for (const src of sources) {
118
+ for (const rule of src.rules) {
119
+ const m = rule.when(ctx);
120
+ if (!m.matched)
121
+ continue;
122
+ const text = typeof rule.text === "function" ? rule.text(m, ctx) : rule.text;
123
+ const salience = m.atoms.reduce((s, a) => s + a.salience, 0) * (rule.weight ?? 1);
124
+ entries.push({
125
+ id: `${src.id}/${rule.id}`, source: src.id, rule: rule.id, text,
126
+ atomIds: m.atoms.map((a) => a.id), salience, tags: rule.tags,
127
+ });
128
+ }
129
+ }
130
+ entries.sort((p, q) => q.salience - p.salience || (p.id < q.id ? -1 : 1));
131
+ return { jdUt: ctx.jdUt, entries };
132
+ }
133
+ /**
134
+ * Group a {@link Reading}'s entries by the facts they share, so statements about
135
+ * the same atoms surface together rather than scattered through a flat list --
136
+ * the substrate for "everything said about this placement" and for spotting
137
+ * contention. Entries are connected when their cited atoms overlap; an entry
138
+ * citing nothing (an absence rule) stands alone. A group is `contested` when a
139
+ * declared conflicting tag-pair both appear in it.
140
+ *
141
+ * Semantic contradiction is the corpus author's to declare (via `tags` +
142
+ * `conflicts`); the resolver does the bookkeeping, not the judgement.
143
+ *
144
+ * @param reading A reading from {@link interpret}.
145
+ * @param opts Conflicting tag pairs and optional text de-duplication.
146
+ * @returns Groups sorted by descending salience.
147
+ */
148
+ export function reconcile(reading, opts = {}) {
149
+ let entries = reading.entries;
150
+ if (opts.dedupe) {
151
+ const seen = new Set(); // entries arrive salience-sorted: keep first
152
+ entries = entries.filter((e) => (seen.has(e.text) ? false : seen.add(e.text)));
153
+ }
154
+ // Union-find over entries that share an atom id.
155
+ const parent = entries.map((_, i) => i);
156
+ const find = (x) => {
157
+ while (parent[x] !== x) {
158
+ parent[x] = parent[parent[x]];
159
+ x = parent[x];
160
+ }
161
+ return x;
162
+ };
163
+ const firstByAtom = new Map();
164
+ entries.forEach((e, i) => {
165
+ for (const id of e.atomIds) {
166
+ const seen = firstByAtom.get(id);
167
+ if (seen === undefined)
168
+ firstByAtom.set(id, i);
169
+ else
170
+ parent[find(i)] = find(seen);
171
+ }
172
+ });
173
+ const buckets = new Map();
174
+ entries.forEach((e, i) => {
175
+ const r = find(i);
176
+ (buckets.get(r) ?? buckets.set(r, []).get(r)).push(e);
177
+ });
178
+ const conflicts = opts.conflicts ?? [];
179
+ const groups = [...buckets.values()].map((es) => {
180
+ es.sort((a, b) => b.salience - a.salience || (a.id < b.id ? -1 : 1));
181
+ const tags = [...new Set(es.flatMap((e) => e.tags ?? []))];
182
+ return {
183
+ atomIds: [...new Set(es.flatMap((e) => e.atomIds))],
184
+ entries: es, tags,
185
+ contested: conflicts.some(([x, y]) => tags.includes(x) && tags.includes(y)),
186
+ salience: Math.max(...es.map((e) => e.salience)),
187
+ };
188
+ });
189
+ groups.sort((a, b) => b.salience - a.salience
190
+ || (a.atomIds.join() < b.atomIds.join() ? -1 : 1));
191
+ return groups;
192
+ }
@@ -0,0 +1,174 @@
1
+ import type { Chart, Zodiac } from "./chart.js";
2
+ import type { AspectPhase } from "./electional.js";
3
+ import { ChartPattern } from "./patterns.js";
4
+ import { ChartSignature } from "./signature.js";
5
+ import type { Realm, Certainty } from "./provenance.js";
6
+ /** Atom kinds in an {@link InterpretationContext}. */
7
+ export type FactKind = "placement" | "aspect" | "pattern" | "signature" | "angle" | "dispositor" | "reception" | "star" | "lot";
8
+ interface FactAtomBase {
9
+ /** Stable, content-addressable id, e.g. `"placement:mars"` or
10
+ * `"aspect:mars~saturn:square"`. Interpretations cite this. */
11
+ id: string;
12
+ kind: FactKind;
13
+ /** Body ids this atom concerns (empty for body-less signature facets). */
14
+ bodies: string[];
15
+ /** Transparent salience (higher = more prominent); see {@link SalienceWeights}. */
16
+ salience: number;
17
+ /** Plain-language statement of the fact -- no interpretation. */
18
+ text: string;
19
+ }
20
+ export interface PlacementAtom extends FactAtomBase {
21
+ kind: "placement";
22
+ body: string;
23
+ sign: string;
24
+ signDeg: number;
25
+ house: number;
26
+ retrograde: boolean;
27
+ dignities: string[];
28
+ }
29
+ export interface AspectAtom extends FactAtomBase {
30
+ kind: "aspect";
31
+ a: string;
32
+ b: string;
33
+ aspect: string;
34
+ /** Orb from exact, degrees. */
35
+ orb: number;
36
+ /** Applying, separating, or exact -- from the two bodies' speeds. */
37
+ phase: AspectPhase;
38
+ /** Closeness in `[0, 1]`: `1` exact, `0` at the orb limit. */
39
+ strength: number;
40
+ }
41
+ export interface PatternAtom extends FactAtomBase {
42
+ kind: "pattern";
43
+ /** Configuration kind, e.g. `"t_square"`, `"grand_trine"`. */
44
+ pattern: string;
45
+ /** Focal body for a T-square or yod. */
46
+ apex?: string;
47
+ }
48
+ export interface SignatureAtom extends FactAtomBase {
49
+ kind: "signature";
50
+ /** Which facet of the structural signature this states. */
51
+ facet: "element" | "modality" | "sign" | "ruler";
52
+ value: string;
53
+ }
54
+ export interface AngleAtom extends FactAtomBase {
55
+ kind: "angle";
56
+ angle: "asc" | "mc" | "vertex" | "eastPoint";
57
+ sign: string;
58
+ signDeg: number;
59
+ }
60
+ export interface DispositorAtom extends FactAtomBase {
61
+ kind: "dispositor";
62
+ body: string;
63
+ /** The classical ruler of the body's sign (equals `body` when in domicile). */
64
+ dispositor: string;
65
+ /** The body occupies its own domicile -- a chain terminus / final dispositor. */
66
+ final: boolean;
67
+ }
68
+ export interface ReceptionAtom extends FactAtomBase {
69
+ kind: "reception";
70
+ /** The dignities the reception runs through: a single dignity when both
71
+ * bodies receive by the same (`"domicile"`, `"exaltation"`, `"triplicity"`),
72
+ * else a sorted pair for a mixed reception (e.g. `"domicile-exaltation"`). */
73
+ by: string;
74
+ }
75
+ export interface StarAtom extends FactAtomBase {
76
+ kind: "star";
77
+ /** The body conjunct the fixed star. */
78
+ body: string;
79
+ /** Catalog star name (see {@link Engine.starNames}). */
80
+ star: string;
81
+ /** Orb from exact conjunction, degrees. */
82
+ orb: number;
83
+ }
84
+ export interface LotAtom extends FactAtomBase {
85
+ kind: "lot";
86
+ /** Hermetic lot name, e.g. `"fortune"` (see {@link HERMETIC_LOTS}). */
87
+ lot: string;
88
+ sign: string;
89
+ signDeg: number;
90
+ house: number;
91
+ }
92
+ export type FactAtom = PlacementAtom | AspectAtom | PatternAtom | SignatureAtom | AngleAtom | DispositorAtom | ReceptionAtom | StarAtom | LotAtom;
93
+ /** A chart as a flat, ranked list of {@link FactAtom}s. */
94
+ export interface InterpretationContext {
95
+ jdUt: number;
96
+ zodiac: Zodiac;
97
+ /** Atoms sorted by descending {@link FactAtomBase.salience}, then `id`. */
98
+ atoms: FactAtom[];
99
+ /** What the chart is, when supplied via {@link ContextOptions.provenance} --
100
+ * framing for an interpreter (a forecast is provisional, a mythic chart is a
101
+ * symbol, not a biography). */
102
+ realm?: Realm;
103
+ /** How firmly the instant is known. When not `"exact"`, time-sensitive atoms
104
+ * (the Moon, the angles) are damped, since their positions are less certain. */
105
+ certainty?: Certainty;
106
+ }
107
+ /** Additive salience weights. Each contribution is documented at its use site;
108
+ * override any subset through {@link ContextOptions.salience}. */
109
+ export interface SalienceWeights {
110
+ /** Every atom starts here. */
111
+ base: number;
112
+ /** Added when the Sun or Moon is involved. */
113
+ luminary: number;
114
+ /** Added for an angular house (1/4/7/10) or an angle atom. */
115
+ angular: number;
116
+ /** Added to the placement of the Ascendant ruler. */
117
+ chartRuler: number;
118
+ /** Added per essential dignity a body holds. */
119
+ dignity: number;
120
+ /** Added to a hard aspect (conjunction/square/opposition). */
121
+ hardAspect: number;
122
+ /** Base salience of a whole configuration (T-square, grand trine, ...). */
123
+ pattern: number;
124
+ /** Added to a dispositor link (and again when it is a final dispositor). */
125
+ dispositor: number;
126
+ /** Added to a mutual reception. */
127
+ reception: number;
128
+ /** Added to a body's conjunction with a fixed star. */
129
+ star: number;
130
+ /** Added to a Hermetic lot (the Part of Fortune and its companions). */
131
+ lot: number;
132
+ }
133
+ export declare const DEFAULT_SALIENCE: SalienceWeights;
134
+ export interface ContextOptions {
135
+ /** Salience weights to override (merged over {@link DEFAULT_SALIENCE}). */
136
+ salience?: Partial<SalienceWeights>;
137
+ /** Precomputed patterns/signature, to avoid recomputing them. */
138
+ patterns?: ChartPattern[];
139
+ signature?: ChartSignature;
140
+ /** The chart's grounding. Carried onto the context; an inexact `certainty`
141
+ * damps time-sensitive atoms. Wire from {@link realize}'s result. */
142
+ provenance?: {
143
+ realm?: Realm;
144
+ certainty?: Certainty;
145
+ };
146
+ /** Fixed-star conjunctions to project as `star` atoms. The engine does not
147
+ * compute these from a bare {@link Chart} (the star catalog lives in the
148
+ * data pack), so a caller supplies them, e.g. from
149
+ * {@link Engine.starConjunctions}. */
150
+ stars?: {
151
+ body: string;
152
+ star: string;
153
+ orb: number;
154
+ }[];
155
+ /** Hermetic lots to project as `lot` atoms, e.g. from {@link Engine.lots}. */
156
+ lots?: {
157
+ lot: string;
158
+ sign: string;
159
+ signDeg: number;
160
+ house: number;
161
+ }[];
162
+ }
163
+ /**
164
+ * Project a {@link Chart} into a ranked list of {@link FactAtom}s -- the
165
+ * substrate an interpretation layer consumes. Pure and deterministic; computes
166
+ * applying/separating and a normalized strength for each aspect that the bare
167
+ * {@link Chart.aspects} list omits.
168
+ *
169
+ * @param chart A chart from {@link Engine.chart} / {@link Engine.chartAt}.
170
+ * @param opts Salience overrides, orb policy, and precomputed reductions.
171
+ * @returns The {@link InterpretationContext}; `atoms` are sorted by salience.
172
+ */
173
+ export declare function interpretationContext(chart: Chart, opts?: ContextOptions): InterpretationContext;
174
+ export {};