fortune-telling-core 0.1.0__tar.gz

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.
Files changed (140) hide show
  1. fortune_telling_core-0.1.0/.github/workflows/cd.yml +67 -0
  2. fortune_telling_core-0.1.0/.github/workflows/ci.yml +49 -0
  3. fortune_telling_core-0.1.0/.github/workflows/docs.yml +70 -0
  4. fortune_telling_core-0.1.0/.gitignore +22 -0
  5. fortune_telling_core-0.1.0/AGENTS.md +83 -0
  6. fortune_telling_core-0.1.0/JOURNAL.md +1 -0
  7. fortune_telling_core-0.1.0/LICENSE +21 -0
  8. fortune_telling_core-0.1.0/PKG-INFO +171 -0
  9. fortune_telling_core-0.1.0/README.md +139 -0
  10. fortune_telling_core-0.1.0/docs/api/astronomy.md +7 -0
  11. fortune_telling_core-0.1.0/docs/api/core.md +6 -0
  12. fortune_telling_core-0.1.0/docs/api/traditions/astrology.md +29 -0
  13. fortune_telling_core-0.1.0/docs/api/traditions/four_pillars.md +34 -0
  14. fortune_telling_core-0.1.0/docs/api/traditions/nine_star_ki.md +48 -0
  15. fortune_telling_core-0.1.0/docs/api/traditions/tarot.md +20 -0
  16. fortune_telling_core-0.1.0/docs/index.md +57 -0
  17. fortune_telling_core-0.1.0/mkdocs.yml +60 -0
  18. fortune_telling_core-0.1.0/pyproject.toml +95 -0
  19. fortune_telling_core-0.1.0/src/fortune_telling_core/__init__.py +69 -0
  20. fortune_telling_core-0.1.0/src/fortune_telling_core/_null_rng.py +21 -0
  21. fortune_telling_core-0.1.0/src/fortune_telling_core/_parsing.py +45 -0
  22. fortune_telling_core-0.1.0/src/fortune_telling_core/_time.py +24 -0
  23. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/__init__.py +69 -0
  24. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/bodies.py +31 -0
  25. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/deltat.py +41 -0
  26. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/ephemeris/__init__.py +7 -0
  27. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/ephemeris/builtin.py +369 -0
  28. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/ephemeris/builtin_series.py +5879 -0
  29. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/ephemeris/fixed.py +65 -0
  30. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/ephemeris/protocol.py +40 -0
  31. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/errors.py +11 -0
  32. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/julian.py +74 -0
  33. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/nutation.py +59 -0
  34. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/position.py +58 -0
  35. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/solar.py +110 -0
  36. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/solar_terms.py +114 -0
  37. fortune_telling_core-0.1.0/src/fortune_telling_core/astronomy/time_model.py +55 -0
  38. fortune_telling_core-0.1.0/src/fortune_telling_core/cli.py +236 -0
  39. fortune_telling_core-0.1.0/src/fortune_telling_core/coerce.py +83 -0
  40. fortune_telling_core-0.1.0/src/fortune_telling_core/draw.py +146 -0
  41. fortune_telling_core-0.1.0/src/fortune_telling_core/engine.py +168 -0
  42. fortune_telling_core-0.1.0/src/fortune_telling_core/errors.py +21 -0
  43. fortune_telling_core-0.1.0/src/fortune_telling_core/provenance.py +92 -0
  44. fortune_telling_core-0.1.0/src/fortune_telling_core/reading.py +135 -0
  45. fortune_telling_core-0.1.0/src/fortune_telling_core/request.py +141 -0
  46. fortune_telling_core-0.1.0/src/fortune_telling_core/rng.py +207 -0
  47. fortune_telling_core-0.1.0/src/fortune_telling_core/serde.py +49 -0
  48. fortune_telling_core-0.1.0/src/fortune_telling_core/serde_types.py +9 -0
  49. fortune_telling_core-0.1.0/src/fortune_telling_core/spread.py +129 -0
  50. fortune_telling_core-0.1.0/src/fortune_telling_core/symbols.py +174 -0
  51. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/__init__.py +4 -0
  52. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/__init__.py +58 -0
  53. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/_julian.py +5 -0
  54. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/angles.py +55 -0
  55. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/aspects.py +67 -0
  56. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/ayanamsa.py +13 -0
  57. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/birth.py +79 -0
  58. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/bodies.py +50 -0
  59. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/chart.py +79 -0
  60. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/config.py +36 -0
  61. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/deltat.py +5 -0
  62. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/engine.py +186 -0
  63. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/ephemeris/__init__.py +7 -0
  64. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/ephemeris/builtin.py +5 -0
  65. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/ephemeris/builtin_series.py +29 -0
  66. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/ephemeris/fixed.py +5 -0
  67. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/ephemeris/protocol.py +5 -0
  68. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/errors.py +15 -0
  69. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/houses.py +190 -0
  70. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/nutation.py +9 -0
  71. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/positions.py +83 -0
  72. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/sidereal.py +11 -0
  73. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/spreads.py +17 -0
  74. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/astrology/zodiac.py +41 -0
  75. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/__init__.py +43 -0
  76. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/birth.py +54 -0
  77. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/chart.py +82 -0
  78. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/config.py +37 -0
  79. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/deck.py +36 -0
  80. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/engine.py +236 -0
  81. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/luck.py +47 -0
  82. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/pillars.py +88 -0
  83. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/sexagenary.py +40 -0
  84. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/solar_terms.py +26 -0
  85. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/spreads.py +18 -0
  86. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/stems_branches.py +74 -0
  87. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/ten_gods.py +93 -0
  88. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/four_pillars/time_model.py +5 -0
  89. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/__init__.py +49 -0
  90. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/birth.py +47 -0
  91. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/chart.py +53 -0
  92. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/config.py +21 -0
  93. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/deck.py +24 -0
  94. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/engine.py +214 -0
  95. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/lo_shu.py +25 -0
  96. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/spreads.py +14 -0
  97. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/star_calc.py +192 -0
  98. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/nine_star_ki/stars.py +45 -0
  99. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/tarot/__init__.py +26 -0
  100. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/tarot/cards.py +74 -0
  101. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/tarot/engine.py +119 -0
  102. fortune_telling_core-0.1.0/src/fortune_telling_core/traditions/tarot/spreads.py +25 -0
  103. fortune_telling_core-0.1.0/tests/astronomy/__init__.py +1 -0
  104. fortune_telling_core-0.1.0/tests/astronomy/test_builtin_ephemeris_accuracy.py +70 -0
  105. fortune_telling_core-0.1.0/tests/astronomy/test_builtin_series_generation.py +44 -0
  106. fortune_telling_core-0.1.0/tests/astronomy/test_julian_solar.py +46 -0
  107. fortune_telling_core-0.1.0/tests/astronomy/test_solar_terms.py +49 -0
  108. fortune_telling_core-0.1.0/tests/astronomy/test_time_model.py +46 -0
  109. fortune_telling_core-0.1.0/tests/core/test_draw.py +31 -0
  110. fortune_telling_core-0.1.0/tests/core/test_schema_version.py +38 -0
  111. fortune_telling_core-0.1.0/tests/core/test_serde_roundtrip.py +60 -0
  112. fortune_telling_core-0.1.0/tests/core/test_symbols_spread.py +49 -0
  113. fortune_telling_core-0.1.0/tests/test_cli.py +45 -0
  114. fortune_telling_core-0.1.0/tests/test_package.py +5 -0
  115. fortune_telling_core-0.1.0/tests/test_rng.py +31 -0
  116. fortune_telling_core-0.1.0/tests/test_summary_contract.py +107 -0
  117. fortune_telling_core-0.1.0/tests/traditions/__init__.py +1 -0
  118. fortune_telling_core-0.1.0/tests/traditions/astrology/__init__.py +1 -0
  119. fortune_telling_core-0.1.0/tests/traditions/astrology/test_cast_replay.py +111 -0
  120. fortune_telling_core-0.1.0/tests/traditions/astrology/test_ephemeris.py +23 -0
  121. fortune_telling_core-0.1.0/tests/traditions/astrology/test_houses.py +143 -0
  122. fortune_telling_core-0.1.0/tests/traditions/astrology/test_sidereal_and_leakage.py +62 -0
  123. fortune_telling_core-0.1.0/tests/traditions/astrology/test_zodiac_spread.py +16 -0
  124. fortune_telling_core-0.1.0/tests/traditions/four_pillars/__init__.py +1 -0
  125. fortune_telling_core-0.1.0/tests/traditions/four_pillars/test_cast_replay.py +121 -0
  126. fortune_telling_core-0.1.0/tests/traditions/four_pillars/test_data.py +21 -0
  127. fortune_telling_core-0.1.0/tests/traditions/four_pillars/test_rules.py +32 -0
  128. fortune_telling_core-0.1.0/tests/traditions/nine_star_ki/__init__.py +1 -0
  129. fortune_telling_core-0.1.0/tests/traditions/nine_star_ki/test_data_calc.py +118 -0
  130. fortune_telling_core-0.1.0/tests/traditions/nine_star_ki/test_engine.py +141 -0
  131. fortune_telling_core-0.1.0/tests/traditions/tarot/__init__.py +1 -0
  132. fortune_telling_core-0.1.0/tests/traditions/tarot/test_deck.py +16 -0
  133. fortune_telling_core-0.1.0/tests/traditions/tarot/test_determinism.py +24 -0
  134. fortune_telling_core-0.1.0/tests/traditions/tarot/test_replay.py +31 -0
  135. fortune_telling_core-0.1.0/tests/traditions/tarot/test_reversals.py +30 -0
  136. fortune_telling_core-0.1.0/tools/ephemeris/download_vsop87d.py +70 -0
  137. fortune_telling_core-0.1.0/tools/ephemeris/generate_builtin_series.py +244 -0
  138. fortune_telling_core-0.1.0/tools/ephemeris/sources/MANIFEST.sha256 +15 -0
  139. fortune_telling_core-0.1.0/tools/ephemeris/sources/README.md +32 -0
  140. fortune_telling_core-0.1.0/tools/ephemeris/sources/meeus_tables.py +318 -0
@@ -0,0 +1,67 @@
1
+ name: CD
2
+
3
+ # Publish to PyPI on a published GitHub Release using PyPI Trusted Publishing
4
+ # (OIDC). No long-lived API token is stored: the publish job exchanges a
5
+ # short-lived GitHub OIDC token for a PyPI upload credential. Configure the
6
+ # trusted publisher once at https://pypi.org/manage/account/publishing/ with:
7
+ # owner: moriyoshi repository: fortune-telling-core
8
+ # workflow: cd.yml environment: pypi
9
+ on:
10
+ release:
11
+ types: [published]
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ build:
18
+ name: Build distributions
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Checkout
22
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
23
+ with:
24
+ # hatch-vcs derives the package version from tags, so fetch full
25
+ # history and tags rather than a shallow checkout.
26
+ fetch-depth: 0
27
+
28
+ - name: Set up Python
29
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
30
+ with:
31
+ python-version: "3.12"
32
+
33
+ - name: Install release dependencies
34
+ run: python -m pip install --upgrade ".[release]"
35
+
36
+ - name: Build sdist + wheel
37
+ run: python -m build
38
+
39
+ - name: Check distributions
40
+ run: |
41
+ python -m twine check dist/*
42
+
43
+ - name: Upload distributions
44
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+ if-no-files-found: error
49
+
50
+ publish:
51
+ name: Publish to PyPI
52
+ needs: build
53
+ runs-on: ubuntu-latest
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/p/fortune-telling-core
57
+ permissions:
58
+ id-token: write # OIDC token for PyPI Trusted Publishing
59
+ steps:
60
+ - name: Download distributions
61
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
62
+ with:
63
+ name: dist
64
+ path: dist/
65
+
66
+ - name: Publish
67
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
@@ -0,0 +1,49 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ # Cancel superseded runs on the same ref to save CI minutes.
10
+ concurrency:
11
+ group: ci-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ check:
19
+ name: lint + type-check + test (py${{ matrix.python-version }})
20
+ runs-on: ubuntu-latest
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ python-version: ["3.12", "3.13", "3.14"]
25
+ steps:
26
+ - name: Checkout
27
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
28
+
29
+ - name: Set up Python ${{ matrix.python-version }}
30
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
31
+ with:
32
+ python-version: ${{ matrix.python-version }}
33
+ cache: pip
34
+ cache-dependency-path: pyproject.toml
35
+
36
+ - name: Install
37
+ run: python -m pip install -e ".[dev]"
38
+
39
+ - name: Format check (ruff)
40
+ run: python -m ruff format --check .
41
+
42
+ - name: Lint (ruff)
43
+ run: python -m ruff check --output-format=github .
44
+
45
+ - name: Type-check (mypy)
46
+ run: python -m mypy src tests
47
+
48
+ - name: Test (pytest)
49
+ run: python -m pytest
@@ -0,0 +1,70 @@
1
+ name: Docs
2
+
3
+ # Build the MkDocs site and deploy to GitHub Pages. Deployment uses the GitHub
4
+ # Pages OIDC flow (id-token: write) rather than pushing to a gh-pages branch.
5
+ # Enable once under repository Settings -> Pages -> Build and deployment ->
6
+ # Source: GitHub Actions.
7
+ on:
8
+ push:
9
+ branches: [main]
10
+ paths:
11
+ - "docs/**"
12
+ - "src/**"
13
+ - "mkdocs.yml"
14
+ - "pyproject.toml"
15
+ - ".github/workflows/docs.yml"
16
+ workflow_dispatch:
17
+
18
+ # Cancel superseded runs, but let an in-progress deployment finish.
19
+ concurrency:
20
+ group: pages
21
+ cancel-in-progress: false
22
+
23
+ permissions:
24
+ contents: read
25
+
26
+ jobs:
27
+ build:
28
+ name: Build site
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - name: Checkout
32
+ uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
33
+
34
+ - name: Set up Python
35
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
36
+ with:
37
+ python-version: "3.12"
38
+ cache: pip
39
+ cache-dependency-path: pyproject.toml
40
+
41
+ - name: Install
42
+ run: python -m pip install -e ".[docs]"
43
+
44
+ - name: Build (strict)
45
+ run: mkdocs build --strict
46
+
47
+ - name: Configure Pages
48
+ uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
49
+
50
+ - name: Upload Pages artifact
51
+ uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
52
+ with:
53
+ path: site
54
+
55
+ deploy:
56
+ name: Deploy to Pages
57
+ needs: build
58
+ # Only publish from the default branch, not from manual runs on other refs.
59
+ if: github.ref == 'refs/heads/main'
60
+ runs-on: ubuntu-latest
61
+ environment:
62
+ name: github-pages
63
+ url: ${{ steps.deployment.outputs.page_url }}
64
+ permissions:
65
+ pages: write # publish to GitHub Pages
66
+ id-token: write # OIDC verification of the deployment origin
67
+ steps:
68
+ - name: Deploy
69
+ id: deployment
70
+ uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
@@ -0,0 +1,22 @@
1
+ .agents-workspace/
2
+ .cache/
3
+ .venv/
4
+ __pycache__/
5
+ *.py[cod]
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .mypy_cache/
10
+ .coverage
11
+ coverage/
12
+ dist/
13
+ build/
14
+ site/
15
+ .env
16
+ .env.*
17
+ !.env.example
18
+ *.swp
19
+ *.swo
20
+ .DS_Store
21
+ Thumbs.db
22
+ __MACOSX
@@ -0,0 +1,83 @@
1
+ # Documents for both humans and coding agents
2
+
3
+ * [README.md](./README.md)
4
+
5
+ # Documents for coding agents
6
+
7
+ * [./.agents/docs/OVERVIEW.md](./.agents/docs/OVERVIEW.md) ... project overview.
8
+ * [./.agents/docs/ARCHITECTURE.md](./.agents/docs/ARCHITECTURE.md) ... library structure and design notes.
9
+ * [./.agents/docs/JOURNAL.md](./.agents/docs/JOURNAL.md) ... chronological findings, decisions, and work history.
10
+ * [./.agents/docs/LTM/INDEX.md](./.agents/docs/LTM/INDEX.md) ... long-term memory index for durable project knowledge under `./.agents/docs/LTM/`.
11
+ * [./.agents/docs/TODO.md](./.agents/docs/TODO.md) ... open to-do items, including the current implementation handoff: extract shared solar-term/time-model code into `astronomy`, then build the Nine Star Ki backend per the `JOURNAL.md` 2026-06-12 Nine Star Ki design entry.
12
+ * [./.agents/skills/](./.agents/skills/) ... local memory-maintenance skills for consolidating `JOURNAL.md`, LTM documents, and canonical project docs.
13
+
14
+ # Rules and protocols
15
+
16
+ ## General
17
+
18
+ * This repository is the home for `fortune-telling-core`, a Python library for composable fortune-telling systems.
19
+ * Keep project knowledge in the agent docs as the implementation evolves.
20
+ * Prefer existing project patterns over introducing new frameworks or conventions.
21
+ * Keep the core library deterministic, typed, and testable. Randomness should be injectable so readings can be reproduced.
22
+ * Do not bake spiritual, cultural, or localisation assumptions into shared primitives. Model traditions and interpretation systems as explicit modules.
23
+ * When a design decision is genuinely ambiguous because established schools or conventions diverge (for example the Nine Star Ki day-star escapement, a house system, or a zodiac/ayanamsa choice), expose it as a configurable option (a config enum, engine argument, or request option) with a sensible documented default, rather than hardcoding one choice. Record the selected value in `Provenance.notes` so a reading stays reproducible and auditable. Use judgement: reserve options for genuine divergence, not every default, to avoid option sprawl.
24
+ * When a durable decision, pitfall, or investigation result matters to future work, append it to `.agents/docs/JOURNAL.md`.
25
+
26
+ ## File Management
27
+
28
+ * Work summaries belong under `./.agents/docs`, not under `/tmp`.
29
+ * Temporary files belong under `./.agents-workspace/tmp`, not under `/tmp`.
30
+ * Never delete user files without permission. Only safe to delete: files you created in the current session under `./.agents-workspace/tmp/`.
31
+ * Keep generated scratch artefacts out of source directories unless they are part of the requested deliverable.
32
+
33
+ ## Building and Testing
34
+
35
+ * The project stack is Python 3.12+ with a `src/` package layout.
36
+ * When fixing a bug, add a focused regression test whenever the codebase has a practical test harness.
37
+ * Do not report a change as complete until the relevant checks have been run, or until you explicitly state why they could not be run.
38
+
39
+ ## Local Lint Gate
40
+
41
+ Before reporting a code change as done, run the project's canonical formatter, linter, type-checker, and tests once those commands exist. Record the current commands in this section when the stack is expanded.
42
+
43
+ Current stack: Python package with `pyproject.toml`, `src/fortune_telling_core`, and `tests`.
44
+
45
+ Always use the project virtualenv at `./.venv` for Python tooling. The bare `python`/`pip` on `PATH` resolves to the pyenv-global interpreter, **not** the venv, so activate the venv (`source .venv/bin/activate`) or call the venv binaries explicitly (`./.venv/bin/python`, `./.venv/bin/pip`). Never `pip install` into the global/pyenv interpreter — this includes `build`, `twine`, `mkdocs`, and the `.[dev]`/`.[docs]`/`.[release]` extras.
46
+
47
+ Canonical commands (run after `source .venv/bin/activate`, or prefix each with `./.venv/bin/`):
48
+
49
+ * Create the virtual environment: `python3 -m venv .venv`
50
+ * Activate it: `source .venv/bin/activate`
51
+ * Install for development: `python -m pip install -e ".[dev]"`
52
+ * Format: `python -m ruff format .`
53
+ * Lint: `python -m ruff check .`
54
+ * Type-check: `python -m mypy src tests`
55
+ * Test: `python -m pytest`
56
+
57
+ The library supports Python 3.12, 3.13, and 3.14. A Hatch matrix runs the suite (and full gate) against each:
58
+
59
+ * All versions, tests only: `hatch run test:run`
60
+ * All versions, full gate (ruff + mypy + pytest): `hatch run test:check`
61
+ * A single version: `hatch run +py=3.14 test:run`
62
+
63
+ Before reporting a change that could affect cross-version behaviour as done, run `hatch run test:check` so all three interpreters are exercised.
64
+
65
+ ## Shell Pitfalls (prezto defaults)
66
+
67
+ The user's shell uses prezto, which sets aliases and options that can break non-interactive scripts:
68
+
69
+ * `cp src dst` may prompt interactively when `dst` exists. Prefer explicit overwrite-safe commands.
70
+ * `cat > file <<'EOF'` and `echo > file` can fail with `file exists` when the target exists. Use the repository editing tools rather than shell redirection for tracked files.
71
+ * `rm file` may prompt for confirmation. Never delete user files unless the task explicitly requires it.
72
+
73
+ ## Git Workflow
74
+
75
+ * Never make discretionary commits. Commit only when the user asks.
76
+ * If commits are requested, sign them with `-S` unless the user gives different instructions.
77
+ * Preserve unrelated work in the tree. Do not revert changes you did not make.
78
+
79
+ ## Documentation
80
+
81
+ * Append new findings to `JOURNAL.md`; do not edit existing entries in place except through established memory-consolidation workflows.
82
+ * In repo-authored documentation (`AGENTS.md`, `README.md`, `.agents/docs/**`), never use full-width parentheses. Use half-width parentheses.
83
+ * Same for full-width colons. Use a half-width colon followed by a space.
@@ -0,0 +1 @@
1
+ - 2026-06-12: Split interpreter packaging prep: made core packages pkgutil namespaces and promoted coerce/serde_types helper modules for external namespace contributors.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 moriyoshi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,171 @@
1
+ Metadata-Version: 2.4
2
+ Name: fortune-telling-core
3
+ Version: 0.1.0
4
+ Summary: Composable, reproducible primitives for fortune-telling systems.
5
+ Author-email: Moriyoshi Koizumi <mozo@mozo.jp>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: astrology,divination,fortune-telling,numerology,tarot
9
+ Classifier: Development Status :: 2 - Pre-Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Requires-Python: >=3.12
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy>=2.1; extra == 'dev'
20
+ Requires-Dist: pytest>=9.0; extra == 'dev'
21
+ Requires-Dist: ruff>=0.15; extra == 'dev'
22
+ Requires-Dist: twine>=6.2; extra == 'dev'
23
+ Provides-Extra: docs
24
+ Requires-Dist: mkdocs-material>=9.7; extra == 'docs'
25
+ Requires-Dist: mkdocs>=1.6.1; extra == 'docs'
26
+ Requires-Dist: mkdocstrings[python]>=1.0; extra == 'docs'
27
+ Requires-Dist: ruff>=0.15; extra == 'docs'
28
+ Provides-Extra: release
29
+ Requires-Dist: build>=1.3; extra == 'release'
30
+ Requires-Dist: twine>=6.2; extra == 'release'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # fortune-telling-core
34
+
35
+ `fortune-telling-core` is a composable, reproducible Python library for building
36
+ fortune-telling and divination systems. It provides a small, deterministic, typed core of
37
+ primitives for readings, symbols, spreads, draws, structural summaries, and provenance, with
38
+ tradition-specific engines layered on top.
39
+
40
+ ## Design Principles
41
+
42
+ - **Deterministic and reproducible.** Randomness enters through one narrow `Rng` protocol,
43
+ and every reading records the exact `Draw` that produced it. A recorded draw can be
44
+ replayed without any randomness.
45
+ - **Tradition-agnostic core.** The core knows only symbols, positions, selections,
46
+ deterministic summaries, and audit metadata. Tradition modules live behind their own
47
+ packages and are not re-exported from the top-level package.
48
+ - **Interpretation belongs to harnesses.** Discretionary meanings, localisation, and
49
+ presentation copy are intentionally outside the library. Consumers can map stable
50
+ symbol ids, position ids, modifiers, summaries, and provenance into their own
51
+ interpretation layer.
52
+ - **Configurable where schools diverge.** Where established schools or conventions
53
+ disagree, such as house systems, zodiac/ayanamsa, time models, or the Nine Star Ki
54
+ day-star escapement, the choice is a documented option recorded in reading provenance.
55
+
56
+ ## Included Systems
57
+
58
+ - **Core**: Tradition-neutral value types, engine contracts, replay, serialisation, and
59
+ provenance.
60
+ - **Astronomy**: Shared, dependency-free astronomy including Julian-day helpers, solar
61
+ terms, time models, the `Ephemeris` protocol, and a pure-Python `BuiltinEphemeris`.
62
+ - **Traditions**: `tarot`, `astrology`, `four_pillars` (BaZi), and `nine_star_ki`.
63
+ Each tradition exposes its engine and deck/spread data from its own subpackage.
64
+
65
+ ## Quick Start
66
+
67
+ ```python
68
+ from fortune_telling_core import RandomRng, ReadingRequest, reading_to_json
69
+ from fortune_telling_core.traditions.tarot import RWS_DECK, SINGLE_CARD, build_engine
70
+
71
+ engine = build_engine()
72
+ request = ReadingRequest(
73
+ deck_id=RWS_DECK.id,
74
+ spread_id=SINGLE_CARD.id,
75
+ )
76
+
77
+ reading = engine.read(request, rng=RandomRng(seed=42))
78
+ payload = reading_to_json(reading)
79
+ ```
80
+
81
+ Computed traditions use `cast()` instead of caller-provided randomness:
82
+
83
+ ```python
84
+ from fortune_telling_core import Querent, ReadingRequest
85
+ from fortune_telling_core.traditions.nine_star_ki import (
86
+ NINE_STAR_KI_DECK,
87
+ NINE_STAR_KI_SPREAD,
88
+ build_engine,
89
+ )
90
+
91
+ engine = build_engine()
92
+ request = ReadingRequest(
93
+ deck_id=NINE_STAR_KI_DECK.id,
94
+ spread_id=NINE_STAR_KI_SPREAD.id,
95
+ querent=Querent(
96
+ id="example",
97
+ display_name="Example",
98
+ attributes={
99
+ "birth_datetime": "1990-05-17T09:30:00+09:00",
100
+ "latitude": "35.6895",
101
+ "longitude": "139.6917",
102
+ },
103
+ ),
104
+ )
105
+
106
+ reading = engine.cast(request)
107
+ ```
108
+
109
+ ## Layout
110
+
111
+ ```text
112
+ .
113
+ ├── AGENTS.md
114
+ ├── README.md
115
+ ├── pyproject.toml
116
+ ├── src/
117
+ │ └── fortune_telling_core/
118
+ ├── docs/
119
+ ├── tests/
120
+ ├── tools/
121
+ └── .agents/
122
+ └── docs/
123
+ ```
124
+
125
+ ## Development
126
+
127
+ This repository uses a `src/` Python package layout.
128
+
129
+ ```bash
130
+ python3 -m venv .venv
131
+ source .venv/bin/activate
132
+ python -m pip install -e ".[dev]"
133
+ python -m pytest
134
+ ```
135
+
136
+ Run the deterministic demo CLI:
137
+
138
+ ```bash
139
+ fortune-telling-demo all
140
+ fortune-telling-demo tarot --seed 7
141
+ fortune-telling-demo nine-star-ki --json --target-year 2026
142
+ ```
143
+
144
+ For computed demos, a `--birth-datetime` without a timezone is interpreted in
145
+ the terminal timezone and serialized with an offset.
146
+
147
+ Build or serve the API documentation:
148
+
149
+ ```bash
150
+ python -m pip install -e ".[docs]"
151
+ mkdocs serve
152
+ mkdocs build --strict
153
+ ```
154
+
155
+ Regenerate the built-in ephemeris series only when changing source tables or
156
+ the truncation threshold. The large public VSOP87D source files are downloaded
157
+ into `.cache/ephemeris/vsop87d/`, an ignored local cache outside `tools/`, and
158
+ verified by checksum:
159
+
160
+ ```bash
161
+ python tools/ephemeris/generate_builtin_series.py --download-missing
162
+ python tools/ephemeris/generate_builtin_series.py --check --download-missing
163
+ ```
164
+
165
+ Canonical agent-facing project notes live in `.agents/docs/`.
166
+
167
+ ## Licence and Dependencies
168
+
169
+ `fortune-telling-core` is MIT licensed and is intended to remain zero-copyleft. The required runtime dependency set is empty.
170
+
171
+ Higher-precision astronomy is bring-your-own through the injectable `Ephemeris` Protocol. Consumers own the licensing review for any ephemeris backend they provide.
@@ -0,0 +1,139 @@
1
+ # fortune-telling-core
2
+
3
+ `fortune-telling-core` is a composable, reproducible Python library for building
4
+ fortune-telling and divination systems. It provides a small, deterministic, typed core of
5
+ primitives for readings, symbols, spreads, draws, structural summaries, and provenance, with
6
+ tradition-specific engines layered on top.
7
+
8
+ ## Design Principles
9
+
10
+ - **Deterministic and reproducible.** Randomness enters through one narrow `Rng` protocol,
11
+ and every reading records the exact `Draw` that produced it. A recorded draw can be
12
+ replayed without any randomness.
13
+ - **Tradition-agnostic core.** The core knows only symbols, positions, selections,
14
+ deterministic summaries, and audit metadata. Tradition modules live behind their own
15
+ packages and are not re-exported from the top-level package.
16
+ - **Interpretation belongs to harnesses.** Discretionary meanings, localisation, and
17
+ presentation copy are intentionally outside the library. Consumers can map stable
18
+ symbol ids, position ids, modifiers, summaries, and provenance into their own
19
+ interpretation layer.
20
+ - **Configurable where schools diverge.** Where established schools or conventions
21
+ disagree, such as house systems, zodiac/ayanamsa, time models, or the Nine Star Ki
22
+ day-star escapement, the choice is a documented option recorded in reading provenance.
23
+
24
+ ## Included Systems
25
+
26
+ - **Core**: Tradition-neutral value types, engine contracts, replay, serialisation, and
27
+ provenance.
28
+ - **Astronomy**: Shared, dependency-free astronomy including Julian-day helpers, solar
29
+ terms, time models, the `Ephemeris` protocol, and a pure-Python `BuiltinEphemeris`.
30
+ - **Traditions**: `tarot`, `astrology`, `four_pillars` (BaZi), and `nine_star_ki`.
31
+ Each tradition exposes its engine and deck/spread data from its own subpackage.
32
+
33
+ ## Quick Start
34
+
35
+ ```python
36
+ from fortune_telling_core import RandomRng, ReadingRequest, reading_to_json
37
+ from fortune_telling_core.traditions.tarot import RWS_DECK, SINGLE_CARD, build_engine
38
+
39
+ engine = build_engine()
40
+ request = ReadingRequest(
41
+ deck_id=RWS_DECK.id,
42
+ spread_id=SINGLE_CARD.id,
43
+ )
44
+
45
+ reading = engine.read(request, rng=RandomRng(seed=42))
46
+ payload = reading_to_json(reading)
47
+ ```
48
+
49
+ Computed traditions use `cast()` instead of caller-provided randomness:
50
+
51
+ ```python
52
+ from fortune_telling_core import Querent, ReadingRequest
53
+ from fortune_telling_core.traditions.nine_star_ki import (
54
+ NINE_STAR_KI_DECK,
55
+ NINE_STAR_KI_SPREAD,
56
+ build_engine,
57
+ )
58
+
59
+ engine = build_engine()
60
+ request = ReadingRequest(
61
+ deck_id=NINE_STAR_KI_DECK.id,
62
+ spread_id=NINE_STAR_KI_SPREAD.id,
63
+ querent=Querent(
64
+ id="example",
65
+ display_name="Example",
66
+ attributes={
67
+ "birth_datetime": "1990-05-17T09:30:00+09:00",
68
+ "latitude": "35.6895",
69
+ "longitude": "139.6917",
70
+ },
71
+ ),
72
+ )
73
+
74
+ reading = engine.cast(request)
75
+ ```
76
+
77
+ ## Layout
78
+
79
+ ```text
80
+ .
81
+ ├── AGENTS.md
82
+ ├── README.md
83
+ ├── pyproject.toml
84
+ ├── src/
85
+ │ └── fortune_telling_core/
86
+ ├── docs/
87
+ ├── tests/
88
+ ├── tools/
89
+ └── .agents/
90
+ └── docs/
91
+ ```
92
+
93
+ ## Development
94
+
95
+ This repository uses a `src/` Python package layout.
96
+
97
+ ```bash
98
+ python3 -m venv .venv
99
+ source .venv/bin/activate
100
+ python -m pip install -e ".[dev]"
101
+ python -m pytest
102
+ ```
103
+
104
+ Run the deterministic demo CLI:
105
+
106
+ ```bash
107
+ fortune-telling-demo all
108
+ fortune-telling-demo tarot --seed 7
109
+ fortune-telling-demo nine-star-ki --json --target-year 2026
110
+ ```
111
+
112
+ For computed demos, a `--birth-datetime` without a timezone is interpreted in
113
+ the terminal timezone and serialized with an offset.
114
+
115
+ Build or serve the API documentation:
116
+
117
+ ```bash
118
+ python -m pip install -e ".[docs]"
119
+ mkdocs serve
120
+ mkdocs build --strict
121
+ ```
122
+
123
+ Regenerate the built-in ephemeris series only when changing source tables or
124
+ the truncation threshold. The large public VSOP87D source files are downloaded
125
+ into `.cache/ephemeris/vsop87d/`, an ignored local cache outside `tools/`, and
126
+ verified by checksum:
127
+
128
+ ```bash
129
+ python tools/ephemeris/generate_builtin_series.py --download-missing
130
+ python tools/ephemeris/generate_builtin_series.py --check --download-missing
131
+ ```
132
+
133
+ Canonical agent-facing project notes live in `.agents/docs/`.
134
+
135
+ ## Licence and Dependencies
136
+
137
+ `fortune-telling-core` is MIT licensed and is intended to remain zero-copyleft. The required runtime dependency set is empty.
138
+
139
+ Higher-precision astronomy is bring-your-own through the injectable `Ephemeris` Protocol. Consumers own the licensing review for any ephemeris backend they provide.
@@ -0,0 +1,7 @@
1
+ # Astronomy
2
+
3
+ Shared, tradition-neutral astronomy used by the computed traditions: Julian-day and delta-T
4
+ conversion, nutation, solar terms, the time-model helpers, and the injectable `Ephemeris` protocol
5
+ with its pure-Python `BuiltinEphemeris` (and `FixedEphemeris` test stub).
6
+
7
+ ::: fortune_telling_core.astronomy
@@ -0,0 +1,6 @@
1
+ # Core API
2
+
3
+ The tradition-agnostic core: value types, the engine contract, randomness, serialisation, and
4
+ errors. These symbols are re-exported from the top-level `fortune_telling_core` package.
5
+
6
+ ::: fortune_telling_core
@@ -0,0 +1,29 @@
1
+ # Astrology
2
+
3
+ A natal-chart engine: tropical or sidereal zodiac, ten planets plus the lunar nodes and the
4
+ Ascendant/Midheaven, Whole Sign / Equal / Placidus houses, and aspects rendered into the summary.
5
+
6
+ ```python
7
+ from fortune_telling_core import Querent, ReadingRequest
8
+ from fortune_telling_core.traditions.astrology import NATAL_CHART, TROPICAL_ZODIAC, build_engine
9
+
10
+ engine = build_engine()
11
+ request = ReadingRequest(
12
+ deck_id=TROPICAL_ZODIAC.id,
13
+ spread_id=NATAL_CHART.id,
14
+ querent=Querent(
15
+ id="sample",
16
+ display_name="Sample",
17
+ attributes={
18
+ "birth_datetime": "1990-01-01T12:00:00+00:00",
19
+ "latitude": "51.5074",
20
+ "longitude": "-0.1278",
21
+ "house_system": "whole_sign",
22
+ },
23
+ ),
24
+ )
25
+
26
+ reading = engine.cast(request)
27
+ ```
28
+
29
+ ::: fortune_telling_core.traditions.astrology