--- name: astral-chart-api description: Use Alex's deployed transit-list-demo API to calculate natal charts, current transits, chart SVGs, and local-time-to-UTC resolution for Astral Hermes tenants. Required whenever a user asks to generate or interpret a chart from birth data. version: 0.1.0 author: Hermes Agent + Alex license: MIT metadata: hermes: tags: [astrology, natal-chart, transits, chart-calculation, ephemeris, transit-list-demo, astral-hermes] related_skills: [astrology-location-time-utc, astrology-rulership-chains-alex] --- # Astral Chart API Use this skill whenever the user asks for any of these: - natal chart - birth chart - chart placements - Ascendant / rising sign - houses / MC - transits - synastry input preparation - “make/generate/calculate a chart” - chart image/SVG ## Hard rule Do **not** try to calculate timezone conversion or chart placements with local Python modules such as `pytz`, `timezonefinder`, `flatlib`, or Swiss Ephemeris inside the tenant. A Maya canary tenant once answered “I can't use the pytz module here” and then offered a generic summary; treat that as a regression. The correct behavior is to call the chart API first or clearly report that the chart service failed. Astral Hermes tenants should use Alex's deployed chart service: ```text https://transit-list-demo.apps.poofc.com ``` If a chart calculation tool/API call is possible, make the API call before interpreting. Do not tell the user “I can’t use pytz” and then offer a generic reading. See `references/maya-canary-chart-api-regression.md` for the tenant regression that led to this rule and the `astral-core@0.1.1` bundle fix. ## Available endpoints ```text GET /api/health GET /api/time/resolve?date=YYYY-MM-DD&time=HH:mm[:ss]&location=LOCATION GET /api/time/resolve?date=YYYY-MM-DD&time=HH:mm[:ss]&timezone=IANA_ZONE GET /api/chart?date=YYYY-MM-DD&time=HH:mm[:ss]&location=LOCATION GET /api/chart?utc=day/month/year/hour/minute/second&coordinates=LAT%20LON GET /api/chart/svg?date=YYYY-MM-DD&time=HH:mm[:ss]&location=LOCATION GET /api/chart/svg?utc=day/month/year/hour/minute/second&coordinates=LAT%20LON GET /api/chart/svg?...&highlight=Body1,Body2 GET /api/chart/svg?...&transit=day/month/year/hour/minute/second&highlight=NatalBody,TransitBody GET /api/transits/current ``` `/api/chart/svg` returns SVG. Telegram usually needs PNG/JPEG for inline image delivery, so use the helper script in this skill to convert SVG to PNG and reply with `MEDIA:/absolute/path.png`. ## Tenant KB autosave (Phase 4) After any successful chart compute, persist to the tenant KB so the next reading can `recall_person` and `load_chart` instead of recomputing or re-asking for birth data. ```bash # Determine person slug (recall first; create if new) KB=~/.hermes/skills/astrology/astral-tenant-kb/scripts/kb.py python3 $KB recall_person "$NAME" >/tmp/p.json # If null, then: python3 $KB save_person "$SLUG" --name "$NAME" --birth '{"date":"...","time":"...","location":"...","utc":"...","coordinates":"...","tz":"..."}' # Compute via /api/chart, then: python3 $KB save_chart "$SLUG" --chart "@/tmp/chart.json" --birth "@/tmp/birth.json" # Returns {"hash": "..."} — pass this hash into astrology-reading-compose so the saved reading # carries chart_hash frontmatter (required for A/B comparison). ``` Rules: - Idempotent by `chart_hash` — if `load_chart` returns a hit, skip recompute. - Never write a chart without a person slug. - This step is owned by the **compose layer** in normal reading flow, not by raw `/api/chart` calls. The chart-image helper script does **not** autosave (charts get cached only when tied to a known person). Some older Magi/Astral code paths may still reference `https://astrodataserver.com/chart` or `/draw`. If those fail with TLS errors such as `CERT_HAS_EXPIRED`, do **not** bypass certificate validation. Move the integration to the self-hosted chart service instead: - Legacy `natal=DD/MM/YYYY/HH/mm&location=LAT LON` maps to `GET /api/chart?utc=DD/MM/YYYY/HH/mm/0&coordinates=LAT%20LON`. - Legacy `/draw` maps to `GET /api/chart/svg?utc=DD/MM/YYYY/HH/mm/0&coordinates=LAT%20LON`. - In VPS apps, keep this behind the app's Express backend and verify with a real SVG/body-size check, not just HTTP status. ## Chart images in Telegram When a chart image would help — natal chart, birth chart, aspect explanation, global transit wheel, biwheel/current transits — generate and send an image, not just text. Telegram delivery rule: - Do **not** rely on Telegram rendering raw SVG inline. - Always fetch the raw SVG from `transit-list-demo` `/api/chart/svg` first and keep it as the source artifact. - For a clean/non-RoughJS chart image, render the raw SVG with `@resvg/resvg-js` and supply the actual astro font file (`/home/avalon/apps/svghanddraw/backend/assets/astro-font/astro-webfont.ttf`). Preprocess `font-family="A"` to `font-family="astro"` for resvg/node font resolution; keep numeric labels on Arial/sans-serif. This preserves the transit-list-demo/Magi SVG style better than ImageMagick, which may ignore embedded WOFF fonts and show letters. - transit-list-demo commits `9c40b9e` and `f04f329` stabilize Telegram chart rendering: planet/zodiac glyphs keep the astro font, house/degree/minute labels use regular DejaVu/Arial numerals, and SVG paths/background circles carry explicit `fill="none"` fallbacks so resvg does not paint a black chart background when CSS inheritance/nth-child selectors are incomplete. - For hand-drawn/stylized chart images, use the `svghanddraw` Rough.js renderer pipeline. - Keep the SVG next to the PNG for audit/editing. Helper script: ```bash node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \ --date 1985-09-26 \ --time 07:14 \ --location 'London, UK' \ --name Alex \ --json ``` Implementation note: this script now renders SVG→PNG with local `@resvg/resvg-js` from the skill directory, not ImageMagick. If it fails with `Cannot find module '@resvg/resvg-js'`, run `npm install` in `~/.hermes/skills/astrology/astral-chart-api/`. It uses `/home/avalon/apps/svghanddraw/backend/assets/astro-font/astro-webfont.ttf` by default; override with `--astro-font /path/to/astro-webfont.ttf` if needed. The script writes files under: ```text ~/.hermes/astral-chart-images/ ``` It prints JSON with: ```json { "pngPath": "/absolute/path.png", "svgPath": "/absolute/path.svg", "media": "MEDIA:/absolute/path.png" } ``` Then send the image by placing the `MEDIA:` value in the final Telegram response. ### Polished styling path: `svghanddraw` Alex's chart styling repo is: ```text https://github.com/firemountain/svghanddraw /home/avalon/apps/svghanddraw ``` Use this repo as the styling reference when chart images need to look good. See `references/svghanddraw-styling.md` for the customization JSON schema, the Telegram-clean recipe, and why the shipped `default.json` preset is wrong for delivery. Its method is not “make the stock SVG CSS louder”; it: 1. gets the raw chart SVG, 2. parses SVG elements/classes with `xmldom`, 3. identifies planets/signs/houses/aspects from SVG class names, 4. renders onto Canvas through Rough.js, 5. applies preset/customization JSON for roughness, opacity, fill/stroke, hachures, and targeted highlights, 6. exports PNG/WebP for delivery. For local POCs, use raw SVG from `transit-list-demo`, not `astrodataserver.com`. A working raw-source test script exists at: ```text /tmp/render-svghanddraw-from-transit.mjs ``` It writes: ```text ~/.hermes/astral-chart-images/alex-raw-svg-transit-list-demo-through-svghanddraw.svg ~/.hermes/astral-chart-images/alex-raw-svg-transit-list-demo-through-svghanddraw.png ``` If future work needs production integration, port the `svghanddraw/backend/chartService.js` fetch layer from legacy `/draw` and `/chart` to: ```text GET https://transit-list-demo.apps.poofc.com/api/chart/svg?... # raw SVG GET https://transit-list-demo.apps.poofc.com/api/chart?... # JSON ``` Do not add heavy styling rules into `transit-list-demo` itself. Keep `transit-list-demo` as the canonical raw SVG/data provider and apply visual rendering downstream. Known svghanddraw/raw-SVG rendering pitfalls discovered with transit-list-demo: - transit-list-demo and Magi SVGs use the real custom astro font with encoded glyph letters such as `F`, `D`, `k`, etc. and `font-family="A"`. Do **not** replace those with generic Unicode glyphs for final renders. - In browser SVG, use the repo font assets (`astro-webfont.woff2`/`.woff`) via `@font-face` just like Magi/transit-list-demo. In server-side node-canvas, use the converted actual font file `backend/assets/astro-font/astro-webfont.ttf` and render `font-family="A"` as the font's internal family `astro`. - transit-list-demo sets `svg{font-size:7px}` via embedded CSS, not `font-size` attributes on every ``; a renderer that defaults text to `10px` makes degree labels look oversized/misaligned. - The svghanddraw default preset has red hachure fill; do not use that preset directly for Telegram chart output. Use a clean preset/customizations with `fillColor:none`, white background, and padding. - For polished exports, use `customizations.backgroundColor` and `customizations.canvasPaddingRatio` if available to avoid Telegram/PNG transparent-black backgrounds and clipped outer labels. ### Highlighted natal aspects The chart renderer already supports Magi-style aspect highlighting through the third argument to `engine.svg([chart1, chart2?, highlight])`. The public SVG endpoint exposes that as `highlight=Body1,Body2`. Use it when the answer is about a specific natal aspect: ```bash node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \ --date 1985-09-26 \ --time 07:14 \ --location 'London, UK' \ --highlight 'Sun,Saturn' \ --label 'Alex Sun Saturn aspect' \ --json ``` For a single natal chart, `highlight=Sun,Saturn` highlights both bodies and the aspect line between them if the aspect exists under the service's orb rules. ### Highlighted transit-to-natal aspects For transits, the chart service draws a biwheel when `transit=` is supplied. Internally the SVG renderer treats chart1 as natal and chart2 as transit, so the highlight order for a biwheel must be: ```text highlight=NatalBody,TransitBody ``` This is intentionally different from the user-facing transit label, which should stay: ```text transiting body → aspect → natal body ``` Example: for user-facing **Mercury Opposition natal Saturn**, draw with `highlight=Saturn,Mercury`: ```bash node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \ --utc '26/9/1985/6/14/0' \ --coordinates '51.5072178 -0.1275862' \ --transit '14/5/2026/21/14/28' \ --highlight 'Saturn,Mercury' \ --label 'Mercury Opposition natal Saturn' \ --json ``` ### Global transit aspect charts For global/current transits, omit natal data and use `--utc now` or a specific UTC date as the single chart. Highlight in normal chart order: ```bash node ~/.hermes/skills/astrology/astral-chart-api/scripts/chart-image-for-telegram.mjs \ --utc now \ --highlight 'Jupiter,Saturn' \ --label 'current Jupiter Saturn global aspect' \ --json ``` ## Astrology knowledge-base topology This skill is one piece of a 3-KB stack that backs every astrology reading. See `references/astro-knowledge-topology.md` for the full map (Astro Sources Wiki at `/home/avalon/astro-sources-wiki` with base id `astro-sources`, Human Design wiki, and the per-user/tenant KB prototype at `~/.hermes/astral-kb-prototype/knowledge/`). The legacy `/home/avalon/wiki` path no longer exists — do not reference it. ## Required workflow for natal charts 1. Extract birth date, local time, and location from the user's message. 2. If anything is missing, ask for the missing field concisely. 3. If the user gave a complete local date/time/location, call: ```bash curl -sS 'https://transit-list-demo.apps.poofc.com/api/chart?date=YYYY-MM-DD&time=HH:mm&location=ENCODED_LOCATION' ``` 4. Use returned placements/points/houses from the API as source truth. 5. Mention the resolved timezone/UTC conversion if present. 6. Then interpret using Alex's astrology style, especially traditional rulership chains. ## Required workflow for current transits Call: ```bash curl -sS 'https://transit-list-demo.apps.poofc.com/api/transits/current' ``` If comparing current transits to a natal chart, calculate the natal chart first, then compare aspects/positions returned by the API. ## Response style For a user who asks “generate my chart” or “make Kathleen's chart,” do this: 1. Confirm the data used. 2. Say the chart was calculated through the Astral chart service. 3. Give compact placements first: - Sun - Moon - Ascendant - MC - key clusters / standout aspects if available 4. Then give interpretation. 5. Avoid giant generic disclaimers. ## Failure behavior If the API call fails: - State that the chart service failed. - Give the exact missing/failed piece if known. - Ask for permission to retry or ask for timezone if location lookup failed. - Do not invent placements. Bad: ```text I can't use pytz here, but I can still interpret your chart... ``` Good: ```text I need the chart service to calculate the actual placements. The location/time lookup failed; can you give me the IANA timezone, e.g. America/Toronto, or should I retry with “Toronto, Ontario, Canada”? ``` ## Example User: ```text Generate a chart for Kathleen Brown, October 19 1994, 2:34 PM, Toronto Ontario. ``` Call: ```bash curl -sS 'https://transit-list-demo.apps.poofc.com/api/chart?date=1994-10-19&time=14:34&location=Toronto%2C%20Ontario%2C%20Canada' ``` Then interpret from the API output, not from memory or guesswork.