{"id":50882367,"url":"https://github.com/anchildress1/save-the-sun","last_synced_at":"2026-06-15T14:01:40.768Z","repository":{"id":362812573,"uuid":"1260819802","full_name":"anchildress1/save-the-sun","owner":"anchildress1","description":"A deduction race against Sköll, the wolf who hunts the sun — name the hidden rune before he does. Built for the DEV 2026 June Solstice Challenge.","archived":false,"fork":false,"pushed_at":"2026-06-13T08:21:20.000Z","size":13724,"stargazers_count":2,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T08:21:56.997Z","etag":null,"topics":["a11y","accessibility","ai-opponent","browser-game","canvas","deduction-game","deterministic-engine","game","game-jam","gemini","javascript","llm","solo-dev","wcag","webgl"],"latest_commit_sha":null,"homepage":"https://savethesun.anchildress1.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anchildress1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-05T23:00:00.000Z","updated_at":"2026-06-13T08:21:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/anchildress1/save-the-sun","commit_stats":null,"previous_names":["anchildress1/save-the-sun"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/anchildress1/save-the-sun","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchildress1%2Fsave-the-sun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchildress1%2Fsave-the-sun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchildress1%2Fsave-the-sun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchildress1%2Fsave-the-sun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anchildress1","download_url":"https://codeload.github.com/anchildress1/save-the-sun/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchildress1%2Fsave-the-sun/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34365597,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a11y","accessibility","ai-opponent","browser-game","canvas","deduction-game","deterministic-engine","game","game-jam","gemini","javascript","llm","solo-dev","wcag","webgl"],"created_at":"2026-06-15T14:01:38.404Z","updated_at":"2026-06-15T14:01:40.760Z","avatar_url":"https://github.com/anchildress1.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e☀️ Save the Sun\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\u003cem\u003eA deduction race against Sköll, the wolf who hunts the sun — name the hidden rune before he does.\u003c/em\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Built for the DEV 2026 June Solstice Challenge.\u003cbr/\u003e\n  Canonical design spec lives under \u003ca href=\"./docs/\"\u003e\u003ccode\u003edocs/\u003c/code\u003e\u003c/a\u003e; see \u003ca href=\"AGENTS.md\"\u003e\u003ccode\u003eAGENTS.md\u003c/code\u003e\u003c/a\u003e for AI agent rules.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"CI GHA Status\" src=\"https://img.shields.io/github/actions/workflow/status/anchildress1/save-the-sun/.github%2Fworkflows%2Fci.yml?style=for-the-badge\u0026logo=github\u0026label=CI\" /\u003e\n  \u003cimg alt=\"CodeQL GHA Status\" src=\"https://img.shields.io/github/actions/workflow/status/anchildress1/save-the-sun/.github%2Fworkflows%2Fcodeql.yml?style=for-the-badge\u0026logo=github\u0026label=CodeQL\" /\u003e\n  \u003cbr/\u003e\n  \u003cimg alt=\"Sonar Quality Gate\" src=\"https://img.shields.io/sonar/quality_gate/anchildress1_save-the-sun?server=https%3A%2F%2Fsonarcloud.io\u0026style=for-the-badge\" /\u003e\n  \u003cimg alt=\"Sonar Coverage\" src=\"https://img.shields.io/sonar/coverage/anchildress1_save-the-sun?server=https%3A%2F%2Fsonarcloud.io\u0026style=for-the-badge\u0026color=limegreen\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/anchildress1/save-the-sun/main/docs/assets/social-banner.webp\" alt=\"A rune stone blazing with golden light at sunrise while Sköll, the great wolf, watches from a dark ridge.\" /\u003e\n\u003c/p\u003e\n\n## Table of Contents\n\n- [About](#about)\n- [Play](#play)\n- [Features](#features)\n- [Tech Stack](#tech-stack)\n- [Architecture](#architecture)\n- [Project Structure](#project-structure)\n- [Getting Started](#getting-started)\n- [Configuration](#configuration)\n- [Security](#security)\n- [How to Contribute](#how-to-contribute)\n- [What's Next](#whats-next)\n- [License](#license)\n- [Acknowledgements](#acknowledgements)\n- [Author](#author)\n\n---\n\n## About\n\nIt's the eve of the longest day, and the dawn must be earned. Twenty-four runes stand; one is the solstice offering. You question the Oracle in plain English, cross runes off by hand, and cast your answer before Sköll names it first. The deduction is rigorous and deterministic — every round is provably winnable through legal Asks alone, and the Oracle never lies — but it's wrapped in a night-to-dawn ritual instead of a logic grid. The night visibly advances as turns pass; the moon sets; dawn gathers at the edge of the world.\n\nDesign intent: [`docs/prd.md`](docs/prd.md) · mechanics: [`docs/game-spec.md`](docs/game-spec.md) · voice: [`docs/ux-copy.md`](docs/ux-copy.md).\n\n---\n\n## Play\n\n- **Live:** [savethesun.anchildress1.dev](https://savethesun.anchildress1.dev/)\n- Desktop-first; the embedded board plays down to 750px wide. Below that, the rite asks for a wider sky.\n\n---\n\n## Features\n\n| Feature            | What it does                                                                                                                                                                                                                 |\n| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Free-text Asks     | Type a yes/no question — group, range, light/dark, hue, or a single rune. Gemini interprets it, echoes its reading, and the engine answers truthfully. Refusals (negation, mixed types, secret-fishing) don't cost the turn. |\n| The Cast           | Naming the rune is a separate, armed action — deliberate, sacred, and uninterruptible. Right wins the round; wrong wastes only the turn.                                                                                     |\n| Sköll              | The wolf runs his own deduction over the same board and races you to the rune. His asks are visible; his reasoning isn't.                                                                                                    |\n| Scry \u0026 Hex         | One-use reactions to his Ask: Scry steals his answer, Hex silences the Oracle and wastes his turn. He holds the same charges against you.                                                                                    |\n| The night advances | Every turn slides the painted moon toward the horizon while dawn gathers at the header's edge. Win and the sun rises; lose and the night never breaks.                                                                       |\n| Resume on reload   | A refresh resumes the same round — same secret, same board order, same crossings, same voiced Oracle line. In-memory only; no accounts, no database.                                                                         |\n| Accessibility      | Whole round playable by keyboard, axe-clean across surfaces (WCAG 2.1 AA), screen-reader narration via status regions, reduced-motion respected.                                                                             |\n| Honest degradation | With all mood graphics and audio failed or off, the game remains fully playable and fair on the plain grid.                                                                                                                  |\n\n---\n\n## Tech Stack\n\n- **App:** [SvelteKit 2](https://svelte.dev/docs/kit) / Svelte 5 (runes), TypeScript, Node.js ≥ 26, ESM\n- **AI:** [Gemini API](https://ai.google.dev/) (`@google/genai`) — interprets Asks for the Oracle and plays Sköll\n- **Tests:** Vitest (unit + browser-mode component), Playwright e2e, `@axe-core/playwright`, Lighthouse CI (pre-push)\n- **Quality:** ESLint, Prettier, svelte-check, commitlint (conventional + RAI), secretlint, Lefthook hooks\n- **CI/CD:** GitHub Actions (CI, CodeQL, Release Please), SonarCloud, Docker → Google Cloud Run\n\n---\n\n## Architecture\n\n```mermaid\nflowchart LR\n    accTitle: Save the Sun architecture\n    accDescr: The browser plays against a SvelteKit server on Cloud Run. A deterministic in-memory engine referees the round, and the Gemini API powers the Oracle's interpretation and Sköll's play.\n\n    B[\"Browser — rune board (Svelte 5)\"]\n    K[\"SvelteKit SSR — Cloud Run\"]\n    E[\"Game engine — per-session referee, in memory\"]\n    G[\"Gemini API\"]\n\n    B --\u003e|\"POST /api/action — Ask / Cast / React / Advance\"| K\n    B --\u003e|\"POST /api/new-game\"| K\n    K --\u003e|\"resolve turn, judge cast\"| E\n    K --\u003e|\"interpret the Ask · play Sköll\"| G\n    E --\u003e|\"truthful yes/no — never the secret\"| K\n```\n\nThe engine holds the secret rune and referees every turn; Gemini only ever interprets language and plays the wolf. The board order, the round token, and the secret live server-side per session — a reload resumes, a new game reshuffles.\n\nDeeper diagrams — turn flow, the voice Live + tool-call loop, and the session lifecycle — live in [`docs/architecture.md`](docs/architecture.md).\n\n---\n\n## Project Structure\n\n```\nsrc/\n  lib/\n    components/        # RuneGrid, RuneCard, Onboarding, EndScreen, ReactionPrompt\n    server/\n      engine/          # deterministic referee: state, queries, reactions, session registry\n      oracle/          # Gemini-backed Oracle — interprets a free-text Ask, answers truthfully\n      skoll/           # the wolf: his deduction floor, his asks, his Gemini prompt\n    assets-webp/       # banners, rune art, UI chrome\n    styles/            # theme tokens (midnight navy + ritual gold)\n  routes/\n    +page.svelte       # the rite: header, board, Oracle panel\n    api/action/        # one POST for Ask / Cast / React / Advance\n    api/new-game/      # fresh secret, fresh layout\ndocs/                  # design spec, mechanics, rune data, voice \u0026 copy, test plan\ntests/                 # unit + component (vitest browser) + e2e (playwright, axe)\n```\n\n---\n\n## Getting Started\n\n```bash\ngit clone git@github.com:anchildress1/save-the-sun.git\ncd save-the-sun\nmake install                 # pnpm install + playwright browsers\ncp .env.example .env         # then add your GEMINI_API_KEY\n# Heads up: saving a server-side file restarts the SSR module (Vite HMR), which clears the\n# in-memory game state — the round's secret re-rolls and /debug resets. Restart for a clean\n# round, or just reload, after a server edit. Client-only edits hot-reload normally.\nmake dev                     # vite dev server\n```\n\n- `make preview` — serve the real production build (the asset pipeline `make dev` skips)\n- `make test` — unit + component suite with coverage\n- `make e2e` — Playwright end-to-end (builds and serves the prod bundle first)\n- `make ai-checks` — format, lint, typecheck, test, build; the full validation gate\n\n---\n\n## Configuration\n\n| Variable         | Required | What it does                                                                                                |\n| ---------------- | -------- | ----------------------------------------------------------------------------------------------------------- |\n| `GEMINI_API_KEY` | Yes      | Server-side only — powers the Oracle and Sköll. Get one at [AI Studio](https://aistudio.google.com/apikey). |\n\nLocal secrets live in `.env` (gitignored). Deployed, the key rides Google Secret Manager via [`deploy.sh`](deploy.sh).\n\n---\n\n## Security\n\n- The secret rune never enters gameplay responses, the client bundle, or the public board seed — the engine alone holds and judges the answer, and Gemini prompts are secret-free by construction (the model interprets language and plays the wolf). The always-on `/debug` stream is the one deliberate exception: it names the secret because following the engine's truth live is its whole point — a spoiler surface by design.\n- `GEMINI_API_KEY` is server-side only: `.env` locally, Secret Manager on Cloud Run. It can never enter the `/debug` stream — every logged string is masked at the sink, and tests assert it.\n- Sessions are an `httpOnly`, `secure`, `sameSite=lax` cookie holding an opaque UUID — no user data, no accounts, nothing durable.\n- `secretlint` runs in the pre-commit hook; CodeQL runs in CI.\n\n---\n\n## How to Contribute\n\n- Branch off `main`; PRs only — nothing lands direct.\n- Conventional Commits enforced by commitlint (with the RAI plugin — AI-assisted commits carry attribution footers).\n- Lefthook runs format, lint, and secret-scan pre-commit; Lighthouse a11y/perf gates pre-push.\n- `make ai-checks` green before you push. Treat warnings as errors, because the linters do.\n\n---\n\n## What's Next\n\n- **v2 — the Oracle speaks:** the Oracle and Sköll are _voiced_ — every line is text in the panel and, with audio on, spoken via Gemini TTS. Hands-free voice input (speak your Ask, interrupt her) is an opt-in layer on top. Design + migration in [`docs/architecture.md`](docs/architecture.md#target-architecture--voice-as-delivery-planned).\n- Sköll escalation taunts wired to how close he is.\n\n---\n\n## License\n\n[Polyform Shield 1.0.0](LICENSE) — read it, run it, learn from it, fork it for fun. The one thing you can't do is take the wolf and sell him against us; \"shield\" means exactly what it sounds like. Not an OSI license, on purpose.\n\n---\n\n## Acknowledgements\n\n- The [DEV](https://dev.to/) June Solstice Challenge, for the deadline-shaped motivation.\n- [Gemini](https://ai.google.dev/) plays both the Oracle and the wolf without ever being trusted with the referee's job.\n- The Elder Futhark, for 24 runes with better lore than any invented alphabet.\n- Footer icons: [simple-icons](https://simpleicons.org/), [Font Awesome Free](https://fontawesome.com/) (CC BY 4.0), [Feather](https://feathericons.com/).\n\n---\n\n## Author\n\n**Ashley Childress**\n\n[![anchildress1.dev](https://img.shields.io/badge/anchildress1.dev-3ca8a6?style=for-the-badge)](https://anchildress1.dev) [![GitHub](https://img.shields.io/badge/GitHub-anchildress1-181717?style=for-the-badge\u0026logo=github)](https://github.com/anchildress1) [![dev.to](https://img.shields.io/badge/dev.to-anchildress1-0A0A0A?style=for-the-badge\u0026logo=devdotto)](https://dev.to/anchildress1) [![LinkedIn](https://img.shields.io/badge/LinkedIn-anchildress1-0A66C2?style=for-the-badge)](https://linkedin.com/in/anchildress1)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanchildress1%2Fsave-the-sun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanchildress1%2Fsave-the-sun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanchildress1%2Fsave-the-sun/lists"}