okhp3-daily-oracle
OverKill Hill P³ · overkillhill.com · github.com/OKHP3
Treat the oracle as a three-layer, once-per-day reading: tarot is the anchor,
horoscope is optional context, and the AI message is a best-effort synthesis.
The user must still receive a meaningful card and message when network services
or the AI key are unavailable.
Scope
| In scope |
Out of scope |
tarotapi.dev card fetch with a deterministic Major Arcana fallback |
Birth charts, natal calculations, or multi-day forecasts |
Optional freehoroscopeapi.com daily horoscope |
Push notifications, external writes, or server-side persistence |
Anthropic message generation through this repo's src/lib/oracle.ts |
Adding a backend, Cloudflare Worker, API route, secure proxy, or database |
Per-user, per-day localStorage cache and graceful degradation |
Non-Claude AI providers or public multi-user secret management |
Existing useOracle, OracleReading, and OracleCard integration |
Replacing the app's state and storage architecture |
Repository boundary: read this first
When working in Kieran's LifeTrkr, preserve these facts:
src/lib/oracle.ts owns tarot, horoscope, Anthropic access, the fallback, and
the oracle-message cache. src/hooks/useOracle.ts orchestrates the reading;
src/components/OracleCard.tsx renders it.
- The cache key is exactly
lifetrkr:{userId}:oracle:{YYYY-MM-DD}. The profile sub is the user ID;
use guest when no profile exists or parsing fails. The date is
new Date().toISOString().split('T')[0], never a locale-formatted date.
- The current tarot fallback uses the full 22-card
MAJOR_ARCANA list and
selects MAJOR_ARCANA[dayOfYear % MAJOR_ARCANA.length]. Do not substitute a
random card or a static single card, and do not describe a 12-card pool as the
repo implementation.
- The current app is deliberately client-only. Do not recommend or add a
Cloudflare Worker, server, proxy, database, or API route as part of this
repository task. A secure public proxy is a separate architecture and is out
of scope here.
Workflow
1. Plan
- Identify whether the task changes the data layer, cache contract, UI, or
validation only.
- Inspect
src/lib/oracle.ts, src/hooks/useOracle.ts, src/types.ts, and
src/components/OracleCard.tsx before editing. Preserve their public names
and data flow.
- Decide the fallback at each layer: tarot must always resolve, horoscope may
become
undefined, and the AI message must fall back to card.meaning_up.
- If adapting this pattern outside the repo, name the app prefix explicitly;
inside this repo, never replace the
lifetrkr namespace with myapp.
2. Validate
Read references/oracle.ts for implementation detail and
assets/oracle-component-template.tsx only when a UI template is needed.
Run the optional public-source smoke test when network access is available:
node .agents/skills/okhp3-daily-oracle/scripts/test-oracle-apis.cjs --sign cancer
A non-zero result can be a transient API or CORS failure; it does not remove
the fallback requirement. Never put keys or .env files in test output.
Verify the cache with a fixed YYYY-MM-DD value and two reads. Confirm the
same user/date returns the same message, another user has an isolated key,
and guest is used without a profile.
Simulate tarot failure, horoscope failure, missing key, AI non-2xx response,
malformed JSON, and offline mode. Each path must render useful output or
omit only the optional horoscope. Report unverified external endpoints.
3. Execute
- Use the existing
useOracle() hook in the app. Keep network and local
storage work in src/lib/oracle.ts; do not move it into a component.
- Fetch tarot and the local celestial values in parallel. Fetch the horoscope
only when
settings.birthSign exists; treat failure as optional.
- Generate the AI message only after the tarot card is available. Cache the
resulting message or its
meaning_up fallback under the exact key above.
- Keep the cache read before any external call. This is what makes refreshes
stable and prevents unnecessary requests.
- Render
OracleReading through the existing OracleCard API. Keep the date
in ISO form and expose moon, season, and horoscope context without making
the visual layer responsible for fetching it.
Portable cache pattern
Use this compact pattern only when adapting the workflow to another app; replace
APP_PREFIX with that app's explicit namespace. In this repository the concrete
key must remain lifetrkr:${userId}:oracle:${today}.
const today = new Date().toISOString().split('T')[0]
const userId = profile?.sub || 'guest'
const cacheKey = `${APP_PREFIX}:${userId}:oracle:${today}`
const cached = localStorage.getItem(cacheKey)
if (cached) return cached
// Generate tarot/context/message, then localStorage.setItem(cacheKey, message)
The user ID provides isolation; the ISO date provides daily stability and
zero-TTL expiry. Do not use toLocaleDateString(), toDateString(), or
Math.random() for the cache or fallback selection.
Data and fallback contract
- Tarot: request
https://tarotapi.dev/api/v1/cards/random?n=1; require a usable card and
fall back deterministically from the 22-card MAJOR_ARCANA array using the
Date.now()/getTime() day-of-year calculation and modulo.
- Horoscope: request
https://freehoroscopeapi.com/api/v1/get-horoscope/daily?sign={sign} only
when a birth sign is set; return null/undefined on failure and keep the
reading usable.
- Oracle message: preserve the function
generateOracleMessage(card, moon, season, mercury, birthSign?) and its
fallback to card.meaning_up.
- Keep the prompt warm, grounded, concise, and non-diagnostic. Treat tarot and
horoscope as reflective content, not medical, legal, financial, or predictive
advice.
Anthropic security boundary
This repository has one intentional exception: src/lib/oracle.ts performs a
direct browser request for a personal app. If using that path, preserve all of
the following:
const apiKey = import.meta.env.VITE_ANTHROPIC_API_KEY
// headers include:
// 'anthropic-dangerous-direct-browser-access': 'true'
The VITE_ value is embedded in the production JavaScript bundle and can be
read by anyone who loads the site. This is acceptable only for a personal or
single-user app whose owner accepts exposure and key rotation risk. It is not a
secure public deployment pattern. Never commit the key, print it, or imply that
Vite hides it. If the user requests public multi-user hardening, explain that a
server-side proxy would be the normal solution but is explicitly outside this
repository task; do not implement it here.
Gotchas
- Check
response.ok and response shape before trusting external JSON.
- Cache only after a complete reading or a deliberate fallback; do not cache a
transient loading state or an error object.
- Keep
userId in the key. A global oracle_${date} key leaks one user's
reading into another user's session.
- Keep the fallback deterministic. A refresh during an outage must not redraw a
different card for the same day.
- Treat public endpoints as optional and CORS-sensitive. The smoke script is a
diagnostic, not a production dependency or uptime guarantee.
- Do not introduce ad hoc storage keys in components. Use the existing
lifetrkr conventions and src/lib/storage.ts where applicable.
Bundled resources
references/oracle.ts — full TypeScript data-layer reference. Read on demand;
it is the implementation depth, not a reason to duplicate app modules.
scripts/test-oracle-apis.cjs — network-dependent tarot/horoscope response
shape check; it does not test Anthropic or prove production availability.
assets/oracle-component-template.tsx — UI-only template aligned with the
repo's OracleReading/OracleCard boundary. Do not copy API calls into it.
Output contract
Return the planned data flow, exact cache key, fallback behavior, files changed,
validation commands and results, and any API/CORS or key-exposure concern. Do
not claim that a live endpoint or benchmark was verified unless it was actually
run.
About
Built by Jamie Hill · OverKill Hill P³
Published at github.com/OKHP3
Part of the OKHP3/skillz Agent Skill library.
MIT License -- free to use, fork, and adapt. A nod to the source is appreciated.
1---2name: okhp3-daily-oracle3description: OverKill Hill P³ daily oracle workflow. Use when building or reviewing a stable reading that combines a tarot card, optional zodiac horoscope, and an AI message. Also activate for daily insight, affirmation, card-draw, or consistent word-of-the-day features in a client-only app. Preserve the repo's ISO cache key, deterministic fallback, graceful degradation, and direct-browser security warning; do not add a backend or proxy to this repository.4license: MIT5---67# okhp3-daily-oracle89**OverKill Hill P³** · [overkillhill.com](https://overkillhill.com) · [github.com/OKHP3](https://github.com/OKHP3)1011Treat the oracle as a three-layer, once-per-day reading: tarot is the anchor,12horoscope is optional context, and the AI message is a best-effort synthesis.13The user must still receive a meaningful card and message when network services14or the AI key are unavailable.1516## Scope1718| In scope | Out of scope |19| --- | --- |20| `tarotapi.dev` card fetch with a deterministic Major Arcana fallback | Birth charts, natal calculations, or multi-day forecasts |21| Optional `freehoroscopeapi.com` daily horoscope | Push notifications, external writes, or server-side persistence |22| Anthropic message generation through this repo's `src/lib/oracle.ts` | Adding a backend, Cloudflare Worker, API route, secure proxy, or database |23| Per-user, per-day `localStorage` cache and graceful degradation | Non-Claude AI providers or public multi-user secret management |24| Existing `useOracle`, `OracleReading`, and `OracleCard` integration | Replacing the app's state and storage architecture |2526## Repository boundary: read this first2728When working in Kieran's LifeTrkr, preserve these facts:2930- `src/lib/oracle.ts` owns tarot, horoscope, Anthropic access, the fallback, and31 the oracle-message cache. `src/hooks/useOracle.ts` orchestrates the reading;32 `src/components/OracleCard.tsx` renders it.33- The cache key is exactly34 `lifetrkr:{userId}:oracle:{YYYY-MM-DD}`. The profile `sub` is the user ID;35 use `guest` when no profile exists or parsing fails. The date is36 `new Date().toISOString().split('T')[0]`, never a locale-formatted date.37- The current tarot fallback uses the full 22-card `MAJOR_ARCANA` list and38 selects `MAJOR_ARCANA[dayOfYear % MAJOR_ARCANA.length]`. Do not substitute a39 random card or a static single card, and do not describe a 12-card pool as the40 repo implementation.41- The current app is deliberately client-only. Do not recommend or add a42 Cloudflare Worker, server, proxy, database, or API route as part of this43 repository task. A secure public proxy is a separate architecture and is out44 of scope here.4546## Workflow4748### 1. Plan49501. Identify whether the task changes the data layer, cache contract, UI, or51 validation only.522. Inspect `src/lib/oracle.ts`, `src/hooks/useOracle.ts`, `src/types.ts`, and53 `src/components/OracleCard.tsx` before editing. Preserve their public names54 and data flow.553. Decide the fallback at each layer: tarot must always resolve, horoscope may56 become `undefined`, and the AI message must fall back to `card.meaning_up`.574. If adapting this pattern outside the repo, name the app prefix explicitly;58 inside this repo, never replace the `lifetrkr` namespace with `myapp`.5960### 2. Validate61621. Read `references/oracle.ts` for implementation detail and63 `assets/oracle-component-template.tsx` only when a UI template is needed.642. Run the optional public-source smoke test when network access is available:6566 ```text67 node .agents/skills/okhp3-daily-oracle/scripts/test-oracle-apis.cjs --sign cancer68 ```6970 A non-zero result can be a transient API or CORS failure; it does not remove71 the fallback requirement. Never put keys or `.env` files in test output.723. Verify the cache with a fixed `YYYY-MM-DD` value and two reads. Confirm the73 same user/date returns the same message, another user has an isolated key,74 and `guest` is used without a profile.754. Simulate tarot failure, horoscope failure, missing key, AI non-2xx response,76 malformed JSON, and offline mode. Each path must render useful output or77 omit only the optional horoscope. Report unverified external endpoints.7879### 3. Execute80811. Use the existing `useOracle()` hook in the app. Keep network and local82 storage work in `src/lib/oracle.ts`; do not move it into a component.832. Fetch tarot and the local celestial values in parallel. Fetch the horoscope84 only when `settings.birthSign` exists; treat failure as optional.853. Generate the AI message only after the tarot card is available. Cache the86 resulting message or its `meaning_up` fallback under the exact key above.874. Keep the cache read before any external call. This is what makes refreshes88 stable and prevents unnecessary requests.895. Render `OracleReading` through the existing `OracleCard` API. Keep the date90 in ISO form and expose moon, season, and horoscope context without making91 the visual layer responsible for fetching it.9293## Portable cache pattern9495Use this compact pattern only when adapting the workflow to another app; replace96`APP_PREFIX` with that app's explicit namespace. In this repository the concrete97key must remain `lifetrkr:${userId}:oracle:${today}`.9899```typescript100const today = new Date().toISOString().split('T')[0]101const userId = profile?.sub || 'guest'102const cacheKey = `${APP_PREFIX}:${userId}:oracle:${today}`103const cached = localStorage.getItem(cacheKey)104if (cached) return cached105// Generate tarot/context/message, then localStorage.setItem(cacheKey, message)106```107108The user ID provides isolation; the ISO date provides daily stability and109zero-TTL expiry. Do not use `toLocaleDateString()`, `toDateString()`, or110`Math.random()` for the cache or fallback selection.111112## Data and fallback contract113114- Tarot: request115 `https://tarotapi.dev/api/v1/cards/random?n=1`; require a usable card and116 fall back deterministically from the 22-card `MAJOR_ARCANA` array using the117 `Date.now()`/`getTime()` day-of-year calculation and modulo.118- Horoscope: request119 `https://freehoroscopeapi.com/api/v1/get-horoscope/daily?sign={sign}` only120 when a birth sign is set; return `null`/`undefined` on failure and keep the121 reading usable.122- Oracle message: preserve the function123 `generateOracleMessage(card, moon, season, mercury, birthSign?)` and its124 fallback to `card.meaning_up`.125- Keep the prompt warm, grounded, concise, and non-diagnostic. Treat tarot and126 horoscope as reflective content, not medical, legal, financial, or predictive127 advice.128129## Anthropic security boundary130131This repository has one intentional exception: `src/lib/oracle.ts` performs a132direct browser request for a personal app. If using that path, preserve all of133the following:134135```typescript136const apiKey = import.meta.env.VITE_ANTHROPIC_API_KEY137// headers include:138// 'anthropic-dangerous-direct-browser-access': 'true'139```140141The `VITE_` value is embedded in the production JavaScript bundle and can be142read by anyone who loads the site. This is acceptable only for a personal or143single-user app whose owner accepts exposure and key rotation risk. It is not a144secure public deployment pattern. Never commit the key, print it, or imply that145Vite hides it. If the user requests public multi-user hardening, explain that a146server-side proxy would be the normal solution but is explicitly outside this147repository task; do not implement it here.148149## Gotchas150151- Check `response.ok` and response shape before trusting external JSON.152- Cache only after a complete reading or a deliberate fallback; do not cache a153 transient loading state or an error object.154- Keep `userId` in the key. A global `oracle_${date}` key leaks one user's155 reading into another user's session.156- Keep the fallback deterministic. A refresh during an outage must not redraw a157 different card for the same day.158- Treat public endpoints as optional and CORS-sensitive. The smoke script is a159 diagnostic, not a production dependency or uptime guarantee.160- Do not introduce ad hoc storage keys in components. Use the existing161 `lifetrkr` conventions and `src/lib/storage.ts` where applicable.162163## Bundled resources164165- `references/oracle.ts` — full TypeScript data-layer reference. Read on demand;166 it is the implementation depth, not a reason to duplicate app modules.167- `scripts/test-oracle-apis.cjs` — network-dependent tarot/horoscope response168 shape check; it does not test Anthropic or prove production availability.169- `assets/oracle-component-template.tsx` — UI-only template aligned with the170 repo's `OracleReading`/`OracleCard` boundary. Do not copy API calls into it.171172## Output contract173174Return the planned data flow, exact cache key, fallback behavior, files changed,175validation commands and results, and any API/CORS or key-exposure concern. Do176not claim that a live endpoint or benchmark was verified unless it was actually177run.178179## About180181Built by [Jamie Hill](https://overkillhill.com) · [OverKill Hill P³](https://overkillhill.com)182Published at [github.com/OKHP3](https://github.com/OKHP3)183Part of the [OKHP3/skillz](https://github.com/OKHP3/skillz) Agent Skill library.184MIT License -- free to use, fork, and adapt. A nod to the source is appreciated.