strathmark 2.0.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 (209) hide show
  1. strathmark-2.0.0/.gitattributes +8 -0
  2. strathmark-2.0.0/.github/REPO_METADATA.md +64 -0
  3. strathmark-2.0.0/.github/workflows/ci.yml +250 -0
  4. strathmark-2.0.0/.github/workflows/publish.yml +39 -0
  5. strathmark-2.0.0/.gitignore +52 -0
  6. strathmark-2.0.0/CHANGELOG.md +194 -0
  7. strathmark-2.0.0/CONTRIBUTING.md +64 -0
  8. strathmark-2.0.0/LICENSE +203 -0
  9. strathmark-2.0.0/ONBOARDING.md +127 -0
  10. strathmark-2.0.0/PKG-INFO +243 -0
  11. strathmark-2.0.0/README.md +190 -0
  12. strathmark-2.0.0/TODOS.md +51 -0
  13. strathmark-2.0.0/assets/sTRATHMARK.png +0 -0
  14. strathmark-2.0.0/assets/strathmark_logo.png +0 -0
  15. strathmark-2.0.0/benchmarks/mark_optimizer_capacity.json +23 -0
  16. strathmark-2.0.0/benchmarks/prediction_v2_golden.json +69 -0
  17. strathmark-2.0.0/benchmarks/prediction_v2_manifest.json +50 -0
  18. strathmark-2.0.0/benchmarks/prediction_v2_prelock.json +259 -0
  19. strathmark-2.0.0/benchmarks/prediction_v2_release_attestation.json +10 -0
  20. strathmark-2.0.0/benchmarks/prediction_v2_report.json +441 -0
  21. strathmark-2.0.0/benchmarks/prediction_v2_report.md +19 -0
  22. strathmark-2.0.0/docs/ARCHITECTURE.md +156 -0
  23. strathmark-2.0.0/docs/DEPLOYMENT.md +170 -0
  24. strathmark-2.0.0/docs/OFFLINE_EVIDENCE_SNAPSHOT.md +171 -0
  25. strathmark-2.0.0/docs/PREDICTION_ENGINE_V2.md +280 -0
  26. strathmark-2.0.0/docs/SHADOW_CONSUMER_CONTRACT.md +176 -0
  27. strathmark-2.0.0/docs/STRATHEX_CONSUMER_MIGRATION.md +80 -0
  28. strathmark-2.0.0/docs/cleanup-candidates-2026-05-04.md +47 -0
  29. strathmark-2.0.0/docs/ml-persistence-policy.md +143 -0
  30. strathmark-2.0.0/docs/ml-research-questions.md +12 -0
  31. strathmark-2.0.0/docs/plans/2026-08-11-001-feat-prediction-engine-v2-plan.md +738 -0
  32. strathmark-2.0.0/docs/plans/2026-08-13-001-fix-prediction-v2-hardening-plan.md +683 -0
  33. strathmark-2.0.0/docs/plans/2026-08-13-002-feat-shadow-handicap-operations-plan.md +1059 -0
  34. strathmark-2.0.0/docs/prediction-v2-migration-rehearsal.md +89 -0
  35. strathmark-2.0.0/docs/schema-reality-2026-05-04.md +205 -0
  36. strathmark-2.0.0/docs/solutions/architecture-decisions/absolute-variance-only.md +49 -0
  37. strathmark-2.0.0/docs/solutions/architecture-decisions/dual-store-sqlite-supabase-split.md +154 -0
  38. strathmark-2.0.0/docs/solutions/architecture-decisions/ensemble-predictor-design-decisions.md +80 -0
  39. strathmark-2.0.0/docs/solutions/architecture-decisions/mark-floor-ceiling-invariants.md +42 -0
  40. strathmark-2.0.0/docs/solutions/architecture-decisions/prediction-cascade-order.md +23 -0
  41. strathmark-2.0.0/docs/solutions/architecture-decisions/tournament-weighting-and-decay.md +22 -0
  42. strathmark-2.0.0/docs/solutions/architecture-decisions/tournament-weighting-audit-todo-011.md +20 -0
  43. strathmark-2.0.0/docs/solutions/best-practices/config-env-vars-importlib-reload-pattern.md +92 -0
  44. strathmark-2.0.0/docs/solutions/best-practices/llm-cascade-and-monte-carlo-tuning-2026-04-21.md +21 -0
  45. strathmark-2.0.0/docs/solutions/best-practices/optional-dependency-gating-importorskip.md +93 -0
  46. strathmark-2.0.0/docs/solutions/best-practices/plain-text-output-no-emojis.md +44 -0
  47. strathmark-2.0.0/docs/solutions/best-practices/test-isolation-no-prod-db.md +35 -0
  48. strathmark-2.0.0/docs/solutions/best-practices/workspace-project-disambiguation.md +34 -0
  49. strathmark-2.0.0/docs/solutions/configuration-issues/api-input-validation-gaps.md +38 -0
  50. strathmark-2.0.0/docs/solutions/configuration-issues/env-vars-resolved-at-import-time.md +37 -0
  51. strathmark-2.0.0/docs/solutions/data-integrity/decay-weights-silently-default-to-one.md +36 -0
  52. strathmark-2.0.0/docs/solutions/data-integrity/timeout-results-pollute-baseline.md +36 -0
  53. strathmark-2.0.0/docs/solutions/performance-issues/ollama-cascade-hang-on-unreachable-host.md +43 -0
  54. strathmark-2.0.0/docs/solutions/runtime-errors/ollama-status-cache-race-condition.md +32 -0
  55. strathmark-2.0.0/docs/solutions/test-failures/integration-test-fixture-silent-typeerror.md +37 -0
  56. strathmark-2.0.0/docs/solutions/test-failures/mocking-wrong-import-target.md +32 -0
  57. strathmark-2.0.0/docs/solutions/test-failures/regression-test-must-include-triggering-input.md +69 -0
  58. strathmark-2.0.0/docs/solutions/workflow-patterns/eng-review-cycle-and-todos-handoff.md +80 -0
  59. strathmark-2.0.0/docs/solutions/workflow-patterns/prepare-for-commit-protocol.md +42 -0
  60. strathmark-2.0.0/docs/solutions/workflow-patterns/stale-pycache-diagnostic.md +40 -0
  61. strathmark-2.0.0/docs/wiki/Architecture-Overview.md +32 -0
  62. strathmark-2.0.0/docs/wiki/Deployment.md +55 -0
  63. strathmark-2.0.0/docs/wiki/FAQ.md +61 -0
  64. strathmark-2.0.0/docs/wiki/Fairness-Assessment.md +26 -0
  65. strathmark-2.0.0/docs/wiki/Handicap-Mark-Math.md +30 -0
  66. strathmark-2.0.0/docs/wiki/Home.md +35 -0
  67. strathmark-2.0.0/docs/wiki/Installation.md +35 -0
  68. strathmark-2.0.0/docs/wiki/LLM-Integration.md +22 -0
  69. strathmark-2.0.0/docs/wiki/Persistence-and-Database.md +53 -0
  70. strathmark-2.0.0/docs/wiki/Prediction-Cascade.md +26 -0
  71. strathmark-2.0.0/docs/wiki/Prediction-Engine-V2.md +90 -0
  72. strathmark-2.0.0/docs/wiki/Quick-Start.md +44 -0
  73. strathmark-2.0.0/docs/wiki/README.md +24 -0
  74. strathmark-2.0.0/docs/wiki/REST-API.md +73 -0
  75. strathmark-2.0.0/docs/wiki/Rulebook-Comparison.md +30 -0
  76. strathmark-2.0.0/docs/wiki/STRATHEX-Consumer.md +35 -0
  77. strathmark-2.0.0/docs/wiki/Testing.md +35 -0
  78. strathmark-2.0.0/docs/wiki/Time-Decay-Weighting.md +17 -0
  79. strathmark-2.0.0/docs/wiki/Variance-and-Monte-Carlo.md +21 -0
  80. strathmark-2.0.0/docs/wiki/Wood-and-Diameter-Scaling.md +18 -0
  81. strathmark-2.0.0/docs/wiki/_Footer.md +1 -0
  82. strathmark-2.0.0/docs/wiki/_Sidebar.md +30 -0
  83. strathmark-2.0.0/evaluate_llm_prompts.py +419 -0
  84. strathmark-2.0.0/import_legacy.py +534 -0
  85. strathmark-2.0.0/pyproject.toml +114 -0
  86. strathmark-2.0.0/requirements/api-current.txt +5 -0
  87. strathmark-2.0.0/requirements/api-oldest.txt +5 -0
  88. strathmark-2.0.0/scripts/__init__.py +0 -0
  89. strathmark-2.0.0/scripts/benchmark_mark_optimizer.py +149 -0
  90. strathmark-2.0.0/scripts/freeze_shadow_consumer_contract.py +1475 -0
  91. strathmark-2.0.0/scripts/ingest_proam_results.py +211 -0
  92. strathmark-2.0.0/scripts/measure_baseline_mae.py +161 -0
  93. strathmark-2.0.0/scripts/rekey_against_mnemex.py +323 -0
  94. strathmark-2.0.0/scripts/smoke_installed_distribution.py +243 -0
  95. strathmark-2.0.0/scripts/validate_deployment.py +384 -0
  96. strathmark-2.0.0/scripts/validate_v2.py +702 -0
  97. strathmark-2.0.0/scripts/verify_v2_golden.py +130 -0
  98. strathmark-2.0.0/strathmark/__init__.py +335 -0
  99. strathmark-2.0.0/strathmark/analytics.py +267 -0
  100. strathmark-2.0.0/strathmark/api.py +1789 -0
  101. strathmark-2.0.0/strathmark/auth.py +457 -0
  102. strathmark-2.0.0/strathmark/calculator.py +1288 -0
  103. strathmark-2.0.0/strathmark/config.py +761 -0
  104. strathmark-2.0.0/strathmark/consumer_contract.py +105 -0
  105. strathmark-2.0.0/strathmark/contracts/shadow_consumer_v1.openapi.json +1 -0
  106. strathmark-2.0.0/strathmark/contracts/shadow_consumer_v1.openapi.sha256 +1 -0
  107. strathmark-2.0.0/strathmark/db.py +2139 -0
  108. strathmark-2.0.0/strathmark/decay.py +355 -0
  109. strathmark-2.0.0/strathmark/drift.py +476 -0
  110. strathmark-2.0.0/strathmark/fairness.py +674 -0
  111. strathmark-2.0.0/strathmark/fallback.py +500 -0
  112. strathmark-2.0.0/strathmark/features.py +386 -0
  113. strathmark-2.0.0/strathmark/identity.py +29 -0
  114. strathmark-2.0.0/strathmark/ledger.py +2950 -0
  115. strathmark-2.0.0/strathmark/llm.py +364 -0
  116. strathmark-2.0.0/strathmark/llm_roles.py +244 -0
  117. strathmark-2.0.0/strathmark/loader.py +181 -0
  118. strathmark-2.0.0/strathmark/mark_optimizer.py +359 -0
  119. strathmark-2.0.0/strathmark/migrations/20260504_001_add_source_tracking.sql +100 -0
  120. strathmark-2.0.0/strathmark/migrations/20260504_002_ml_state_tables.sql +206 -0
  121. strathmark-2.0.0/strathmark/migrations/20260504_003_rls_reframe.sql +249 -0
  122. strathmark-2.0.0/strathmark/migrations/20260508_004_atomic_model_swap_and_residual_dedup.sql +88 -0
  123. strathmark-2.0.0/strathmark/migrations/20260811_005_prediction_v2.sql +810 -0
  124. strathmark-2.0.0/strathmark/migrations/20260813_006_prediction_hash_algorithm.down.sql +638 -0
  125. strathmark-2.0.0/strathmark/migrations/20260813_006_prediction_hash_algorithm.sql +649 -0
  126. strathmark-2.0.0/strathmark/migrations/20260813_007_shadow_mirror_contract.down.sql +35 -0
  127. strathmark-2.0.0/strathmark/migrations/20260813_007_shadow_mirror_contract.sql +1688 -0
  128. strathmark-2.0.0/strathmark/migrations/README.md +188 -0
  129. strathmark-2.0.0/strathmark/migrations/prerequisites/prediction_rpc_owner.sql +48 -0
  130. strathmark-2.0.0/strathmark/mirror_contract.py +6 -0
  131. strathmark-2.0.0/strathmark/mnemex.py +348 -0
  132. strathmark-2.0.0/strathmark/models/prediction_v2_core.json +1 -0
  133. strathmark-2.0.0/strathmark/prediction_v2.py +1458 -0
  134. strathmark-2.0.0/strathmark/predictor.py +2868 -0
  135. strathmark-2.0.0/strathmark/provenance.py +24 -0
  136. strathmark-2.0.0/strathmark/residual.py +752 -0
  137. strathmark-2.0.0/strathmark/shadow.py +498 -0
  138. strathmark-2.0.0/strathmark/sqlite_utils.py +25 -0
  139. strathmark-2.0.0/strathmark/store.py +1744 -0
  140. strathmark-2.0.0/strathmark/sync.py +492 -0
  141. strathmark-2.0.0/strathmark/utils.py +185 -0
  142. strathmark-2.0.0/strathmark/validation.py +417 -0
  143. strathmark-2.0.0/strathmark/variance.py +770 -0
  144. strathmark-2.0.0/strathmark/visualization.py +129 -0
  145. strathmark-2.0.0/strathmark/wood.py +672 -0
  146. strathmark-2.0.0/tests/__init__.py +8 -0
  147. strathmark-2.0.0/tests/conftest.py +41 -0
  148. strathmark-2.0.0/tests/installed_wheel_shadow_smoke.py +35 -0
  149. strathmark-2.0.0/tests/postgres_rehearsal.py +2465 -0
  150. strathmark-2.0.0/tests/test_analytics.py +80 -0
  151. strathmark-2.0.0/tests/test_analytics_extended.py +231 -0
  152. strathmark-2.0.0/tests/test_api.py +716 -0
  153. strathmark-2.0.0/tests/test_boundary.py +348 -0
  154. strathmark-2.0.0/tests/test_calculator.py +755 -0
  155. strathmark-2.0.0/tests/test_calculator_extended.py +405 -0
  156. strathmark-2.0.0/tests/test_circuit_breaker.py +190 -0
  157. strathmark-2.0.0/tests/test_config.py +203 -0
  158. strathmark-2.0.0/tests/test_config_invariants.py +215 -0
  159. strathmark-2.0.0/tests/test_db.py +243 -0
  160. strathmark-2.0.0/tests/test_db_extended.py +43 -0
  161. strathmark-2.0.0/tests/test_decay.py +91 -0
  162. strathmark-2.0.0/tests/test_decay_extended.py +236 -0
  163. strathmark-2.0.0/tests/test_deployment_fallbacks.py +330 -0
  164. strathmark-2.0.0/tests/test_drift.py +508 -0
  165. strathmark-2.0.0/tests/test_fairness.py +44 -0
  166. strathmark-2.0.0/tests/test_fairness_extended.py +351 -0
  167. strathmark-2.0.0/tests/test_fallback.py +32 -0
  168. strathmark-2.0.0/tests/test_fallback_extended.py +415 -0
  169. strathmark-2.0.0/tests/test_features.py +161 -0
  170. strathmark-2.0.0/tests/test_integration.py +171 -0
  171. strathmark-2.0.0/tests/test_integration_extended.py +363 -0
  172. strathmark-2.0.0/tests/test_ledger.py +1033 -0
  173. strathmark-2.0.0/tests/test_llm.py +217 -0
  174. strathmark-2.0.0/tests/test_llm_roles.py +58 -0
  175. strathmark-2.0.0/tests/test_llm_roles_extended.py +267 -0
  176. strathmark-2.0.0/tests/test_loader.py +30 -0
  177. strathmark-2.0.0/tests/test_loader_extended.py +242 -0
  178. strathmark-2.0.0/tests/test_mark_optimizer.py +243 -0
  179. strathmark-2.0.0/tests/test_ml_state.py +764 -0
  180. strathmark-2.0.0/tests/test_mnemex.py +114 -0
  181. strathmark-2.0.0/tests/test_offline_evidence_snapshot.py +977 -0
  182. strathmark-2.0.0/tests/test_postgres_rehearsal.py +211 -0
  183. strathmark-2.0.0/tests/test_prediction_v2.py +490 -0
  184. strathmark-2.0.0/tests/test_predictor.py +524 -0
  185. strathmark-2.0.0/tests/test_predictor_extended.py +440 -0
  186. strathmark-2.0.0/tests/test_predictor_regression.py +256 -0
  187. strathmark-2.0.0/tests/test_regression.py +289 -0
  188. strathmark-2.0.0/tests/test_residual.py +354 -0
  189. strathmark-2.0.0/tests/test_settlement_revisions.py +1347 -0
  190. strathmark-2.0.0/tests/test_shadow_api.py +2373 -0
  191. strathmark-2.0.0/tests/test_shadow_consumer_contract.py +896 -0
  192. strathmark-2.0.0/tests/test_shadow_mirror_contract.py +563 -0
  193. strathmark-2.0.0/tests/test_shadow_receipts.py +654 -0
  194. strathmark-2.0.0/tests/test_store.py +168 -0
  195. strathmark-2.0.0/tests/test_store_extended.py +337 -0
  196. strathmark-2.0.0/tests/test_sync.py +150 -0
  197. strathmark-2.0.0/tests/test_utils.py +33 -0
  198. strathmark-2.0.0/tests/test_utils_extended.py +234 -0
  199. strathmark-2.0.0/tests/test_validate_v2.py +175 -0
  200. strathmark-2.0.0/tests/test_validation.py +248 -0
  201. strathmark-2.0.0/tests/test_variance.py +256 -0
  202. strathmark-2.0.0/tests/test_variance_extended.py +344 -0
  203. strathmark-2.0.0/tests/test_visualization.py +64 -0
  204. strathmark-2.0.0/tests/test_visualization_extended.py +208 -0
  205. strathmark-2.0.0/tests/test_wood.py +98 -0
  206. strathmark-2.0.0/tests/test_wood_boundary.py +191 -0
  207. strathmark-2.0.0/tests/test_wood_extended.py +351 -0
  208. strathmark-2.0.0/train_model.py +32 -0
  209. strathmark-2.0.0/woodchopping_clean.xlsx +0 -0
@@ -0,0 +1,8 @@
1
+ * text=auto eol=lf
2
+ *.py text eol=lf
3
+ *.toml text eol=lf
4
+ *.yml text eol=lf
5
+ *.md text eol=lf
6
+ *.txt text eol=lf
7
+ *.json text eol=lf
8
+ *.cfg text eol=lf
@@ -0,0 +1,64 @@
1
+ # GitHub Repository Metadata
2
+
3
+ Paste the following into the repository **About** panel on GitHub
4
+ (Settings tab is not required — use the cog icon next to *About* on the
5
+ repo home page).
6
+
7
+ ## Repository description (one line, 350 char limit)
8
+
9
+ > Woodchopping handicap engine — pip-installable calculation core for
10
+ > timbersports tournaments. Computes Standing Block and Underhand marks
11
+ > with a Manual > LLM > ML > Baseline > Panel cascade, 2-year time-decay
12
+ > weighting, absolute ±3 s variance, and Monte Carlo fairness validation.
13
+ > Extracted from STRATHEX.
14
+
15
+ ## Website
16
+
17
+ ```
18
+ https://pypi.org/project/strathmark/
19
+ ```
20
+
21
+ (leave blank until the first PyPI release; the source URL in
22
+ `pyproject.toml` already points at the GitHub repo)
23
+
24
+ ## Topics / tags
25
+
26
+ Apply these topic tags via the repo **About -> ⚙** settings. GitHub
27
+ topics are lowercase, hyphen-separated, max 50 per repo. These are the
28
+ tags that keep STRATHMARK discoverable to both the timbersports
29
+ community and the Python/ML community:
30
+
31
+ ### Sport-domain tags
32
+
33
+ - `timbersports`
34
+ - `woodchopping`
35
+ - `lumberjack`
36
+ - `axemen`
37
+ - `axe`
38
+ - `chainsaw`
39
+ - `tournament`
40
+ - `handicap`
41
+ - `handicapping`
42
+
43
+ ### Technical / discovery tags
44
+
45
+ - `python`
46
+ - `pandas`
47
+ - `numpy`
48
+ - `xgboost`
49
+ - `machine-learning`
50
+ - `monte-carlo`
51
+ - `monte-carlo-simulation`
52
+ - `fastapi`
53
+ - `ollama`
54
+ - `llm`
55
+ - `sports-analytics`
56
+ - `fairness`
57
+ - `pip-package`
58
+ - `calculation-engine`
59
+
60
+ ## Social preview image
61
+
62
+ Use `assets/strathmark_logo.png` (already checked into the repo) as the
63
+ social preview image under **Settings -> Social preview**. Recommended
64
+ upload size is 1280x640; the current logo can be padded to that canvas.
@@ -0,0 +1,250 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ name: Lint (ruff)
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Set up Python
17
+ uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ - name: Install ruff
22
+ run: pip install ruff
23
+
24
+ - name: Ruff check
25
+ run: ruff check .
26
+
27
+ - name: Ruff format check
28
+ run: ruff format --check .
29
+
30
+ test:
31
+ name: Test (${{ matrix.os }} / Python ${{ matrix.python-version }})
32
+ runs-on: ${{ matrix.os }}
33
+ strategy:
34
+ fail-fast: false
35
+ matrix:
36
+ os: [ubuntu-latest, windows-latest]
37
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+
41
+ - name: Set up Python ${{ matrix.python-version }}
42
+ uses: actions/setup-python@v5
43
+ with:
44
+ python-version: ${{ matrix.python-version }}
45
+
46
+ - name: Install package with dev extras
47
+ run: pip install -e ".[dev]"
48
+
49
+ - name: Check dependency consistency
50
+ run: pip check
51
+
52
+ - name: Run pytest with coverage
53
+ # Supabase-backed db tests are gated behind STRATHMARK_TEST_DB and
54
+ # skip by default. ML predictor paths fall back to baseline when
55
+ # xgboost/lightgbm/sklearn are not installed (they live in the [ml]
56
+ # extra and are not pulled in by [dev]).
57
+ run: pytest tests/ -v --tb=short --cov=strathmark --cov-report=term-missing
58
+
59
+ api:
60
+ name: API contract (${{ matrix.compatible-set }})
61
+ runs-on: ubuntu-latest
62
+ needs: lint
63
+ strategy:
64
+ fail-fast: false
65
+ matrix:
66
+ compatible-set: [oldest, current]
67
+ steps:
68
+ - uses: actions/checkout@v4
69
+
70
+ - name: Set up Python
71
+ uses: actions/setup-python@v5
72
+ with:
73
+ python-version: "3.12"
74
+
75
+ - name: Install oldest compatible API dependency set
76
+ if: matrix.compatible-set == 'oldest'
77
+ run: pip install -e ".[dev,api]" -r requirements/api-oldest.txt
78
+
79
+ - name: Install current compatible API dependency set
80
+ if: matrix.compatible-set == 'current'
81
+ run: pip install -e ".[dev,api]" -r requirements/api-current.txt
82
+
83
+ - name: Check dependency consistency
84
+ run: pip check
85
+
86
+ - name: Run API contract tests
87
+ run: pytest tests/test_api.py tests/test_shadow_api.py tests/test_shadow_consumer_contract.py -v --tb=short
88
+
89
+ optional-ml:
90
+ name: Optional ML artifact and promotion gates (Ubuntu / Python 3.12)
91
+ runs-on: ubuntu-latest
92
+ needs: lint
93
+ steps:
94
+ - uses: actions/checkout@v4
95
+
96
+ - name: Set up Python
97
+ uses: actions/setup-python@v5
98
+ with:
99
+ python-version: "3.12"
100
+
101
+ - name: Install package with optional ML dependencies
102
+ run: pip install -e ".[dev,ml]"
103
+
104
+ - name: Check dependency consistency
105
+ run: pip check
106
+
107
+ - name: Run residual artifact and temporal validation tests
108
+ run: pytest tests/test_residual.py tests/test_validation.py -v --tb=short
109
+
110
+ validation-artifact:
111
+ name: Verify Prediction V2 release evidence (Ubuntu / Python 3.12)
112
+ runs-on: ubuntu-latest
113
+ needs: lint
114
+ steps:
115
+ - uses: actions/checkout@v4
116
+
117
+ - name: Set up Python
118
+ uses: actions/setup-python@v5
119
+ with:
120
+ python-version: "3.12"
121
+
122
+ - name: Install core validation dependencies
123
+ run: pip install -e ".[dev]"
124
+
125
+ - name: Check dependency consistency
126
+ run: pip check
127
+
128
+ - name: Verify persisted report and checksummed artifact
129
+ run: python scripts/validate_v2.py --verify-release
130
+
131
+ - name: Exercise release-gate contracts
132
+ run: pytest tests/test_validate_v2.py tests/test_validation.py -v --tb=short
133
+
134
+ - name: Verify runtime provider loads the packaged core
135
+ run: python -c "from datetime import date; from strathmark.predictor import FilePredictionProvider; b=FilePredictionProvider().snapshot(date(2026,8,11)); assert b.core is not None and b.source == 'package' and b.core.model_version == 'prediction-v2-core-20260207'"
136
+
137
+ golden:
138
+ name: Prediction V2 golden (${{ matrix.os }} / Python 3.12)
139
+ runs-on: ${{ matrix.os }}
140
+ needs: lint
141
+ strategy:
142
+ fail-fast: false
143
+ matrix:
144
+ os: [ubuntu-latest, windows-latest]
145
+ steps:
146
+ - uses: actions/checkout@v4
147
+
148
+ - name: Set up Python
149
+ uses: actions/setup-python@v5
150
+ with:
151
+ python-version: "3.12"
152
+
153
+ - name: Install core test dependencies
154
+ run: pip install -e ".[dev]"
155
+
156
+ - name: Check dependency consistency
157
+ run: pip check
158
+
159
+ - name: Verify normalized public audit output
160
+ run: python scripts/verify_v2_golden.py
161
+
162
+ optimizer-capacity:
163
+ name: Optimizer capacity (Ubuntu / Python 3.12)
164
+ runs-on: ubuntu-latest
165
+ needs: lint
166
+ steps:
167
+ - uses: actions/checkout@v4
168
+
169
+ - name: Set up Python
170
+ uses: actions/setup-python@v5
171
+ with:
172
+ python-version: "3.12"
173
+
174
+ - name: Install core dependencies
175
+ run: pip install -e .
176
+
177
+ - name: Check dependency consistency
178
+ run: pip check
179
+
180
+ - name: Enforce 64-competitor optimizer budget
181
+ run: python scripts/benchmark_mark_optimizer.py --output optimizer-capacity-ci.json
182
+
183
+ - name: Upload capacity measurement
184
+ uses: actions/upload-artifact@v4
185
+ with:
186
+ name: optimizer-capacity-ubuntu-python312
187
+ path: optimizer-capacity-ci.json
188
+
189
+ postgres-migration-rehearsal:
190
+ name: Disposable PostgreSQL migration rehearsal
191
+ runs-on: ubuntu-latest
192
+ services:
193
+ postgres:
194
+ image: postgres:18
195
+ env:
196
+ POSTGRES_USER: rehearsal
197
+ POSTGRES_PASSWORD: rehearsal-ci-only
198
+ POSTGRES_DB: strathmark_rehearsal_controller
199
+ ports:
200
+ - 5432:5432
201
+ options: >-
202
+ --health-cmd "pg_isready -U rehearsal -d strathmark_rehearsal_controller"
203
+ --health-interval 5s
204
+ --health-timeout 5s
205
+ --health-retries 12
206
+ env:
207
+ STRATHMARK_RUN_POSTGRES_REHEARSAL: "1"
208
+ STRATHMARK_REHEARSAL_DSN: postgresql://rehearsal:rehearsal-ci-only@127.0.0.1:5432/strathmark_rehearsal_controller
209
+ steps:
210
+ - uses: actions/checkout@v4
211
+
212
+ - name: Set up Python
213
+ uses: actions/setup-python@v5
214
+ with:
215
+ python-version: "3.12"
216
+
217
+ - name: Install test dependencies and PostgreSQL client
218
+ run: |
219
+ pip install -e ".[dev]"
220
+ sudo apt-get update
221
+ sudo apt-get install --yes postgresql-client
222
+
223
+ - name: Exercise migrations 005 and 006 against disposable PostgreSQL
224
+ run: pytest tests/test_postgres_rehearsal.py -v --tb=short
225
+
226
+ build:
227
+ name: Installed ${{ matrix.kind }} (${{ matrix.os }} / Python 3.12)
228
+ runs-on: ${{ matrix.os }}
229
+ needs: [lint, test, api, optional-ml, validation-artifact, golden, optimizer-capacity, postgres-migration-rehearsal]
230
+ strategy:
231
+ fail-fast: false
232
+ matrix:
233
+ os: [ubuntu-latest, windows-latest]
234
+ kind: [wheel, sdist]
235
+ steps:
236
+ - uses: actions/checkout@v4
237
+
238
+ - name: Set up Python
239
+ uses: actions/setup-python@v5
240
+ with:
241
+ python-version: "3.12"
242
+
243
+ - name: Install build tools
244
+ run: pip install build
245
+
246
+ - name: Build sdist and wheel
247
+ run: python -m build
248
+
249
+ - name: Verify installed distribution outside checkout
250
+ run: python scripts/smoke_installed_distribution.py --kind ${{ matrix.kind }}
@@ -0,0 +1,39 @@
1
+ name: Publish to PyPI
2
+
3
+ # Manual-only for now. Alex triggers releases from the Actions tab.
4
+ # To enable automatic publishing on tag push, uncomment the `push` block.
5
+ on:
6
+ workflow_dispatch:
7
+ # push:
8
+ # tags:
9
+ # - "v*"
10
+
11
+ jobs:
12
+ publish:
13
+ name: Build and publish
14
+ runs-on: ubuntu-latest
15
+ # PyPI trusted publishing requires this environment to be configured
16
+ # in the repo settings AND a matching publisher to be registered on
17
+ # PyPI for the strathmark project. See:
18
+ # https://docs.pypi.org/trusted-publishers/
19
+ # Until that is wired up, this workflow will fail at the upload step.
20
+ environment: pypi
21
+ permissions:
22
+ # Required for PyPI trusted publishing (OIDC token exchange)
23
+ id-token: write
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Set up Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: "3.12"
31
+
32
+ - name: Install build tools
33
+ run: pip install build
34
+
35
+ - name: Build sdist and wheel
36
+ run: python -m build
37
+
38
+ - name: Publish to PyPI
39
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,52 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ *.egg
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ eggs/
12
+ parts/
13
+ var/
14
+ sdist/
15
+ develop-eggs/
16
+ .installed.cfg
17
+ lib/
18
+ lib64/
19
+
20
+ # Virtual environments
21
+ .env
22
+ .venv
23
+ .venv*/
24
+ env/
25
+ venv/
26
+ ENV/
27
+
28
+ # Testing
29
+ .pytest_cache/
30
+ .coverage
31
+ htmlcov/
32
+ .tox/
33
+ .ruff_cache/
34
+
35
+ # IDE
36
+ .vscode/
37
+ .idea/
38
+ *.swp
39
+
40
+ # Distribution
41
+ *.whl
42
+ *.tar.gz
43
+ .gstack/
44
+
45
+ # Local secrets (env-var exports for Supabase, etc.) — never commit
46
+ STRATHMARK API.txt
47
+ *.secrets.txt
48
+
49
+ # Local agent / editor config (per-developer, not portfolio content)
50
+ CLAUDE.md
51
+ .claude/
52
+ AGENTS.md
@@ -0,0 +1,194 @@
1
+ # Changelog
2
+
3
+ All notable changes to STRATHMARK will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [2.0.0] - 2026-08-18
8
+
9
+ Prediction Engine V2 replaces the numeric Manual/LLM/ML/baseline cascade with one
10
+ reproducible, prior-only prediction system while retaining the five legacy result keys
11
+ for compatibility.
12
+
13
+ ### Hardening
14
+
15
+ - Added adversarial causal and artifact validation, trusted-ledger admission and
16
+ interval-aware drift checks, bounded mirror delivery, optimizer oracle/capacity
17
+ evidence, and stronger package/release verification.
18
+ - Drift coverage for V2 is measured from each settled prediction's issued interval;
19
+ residual-quantile reconstruction remains historical behavior only.
20
+ - Reconciled the retired pre-2.0 ensemble TODOs and corrected rounded-gap wording to
21
+ the implemented half-to-even behavior.
22
+ - Excluded undated calibration rows and numeric pseudo-dates, normalized equivalent
23
+ species/gender identities before idempotency hashing, and kept an intentionally
24
+ inactive optional residual from degrading the promoted core.
25
+ - Made active-v2 cloud payloads version-explicit, restored overflow/restart outbox
26
+ recovery, added a historical health cutoff, and guarded the 005-to-006 deploy window
27
+ plus rollback.
28
+ - Preserved the public fairness-simulation admission limit at 4,000,000 cells and
29
+ limited it to one concurrent request per process pending a bounded-batch simulator.
30
+ - Added migration 007's versioned, append-only shadow mirror for immutable receipt
31
+ cores and eligible numeric settle/void revisions. The cloud copy remains optional,
32
+ non-authoritative, stable-ID-only, and excludes operational context and free text.
33
+ The RPC recomputes each delivery digest from an explicit JSON body, validates the
34
+ frozen nested receipt contract, and requires receipt prediction identities to exactly
35
+ match the embedded ledger without duplicates.
36
+ - Published the STRATHEX 7 consumer migration contract and reconciled maintained
37
+ deployment, REST, architecture, installation, and wiki guidance.
38
+
39
+ ### Added
40
+
41
+ - Robust hierarchical log-time core with event and diameter effects, six species
42
+ physical properties, gender/missingness, recency-weighted competitor state, bounded
43
+ trend, cross-event borrowing, and partial pooling for zero/sparse history.
44
+ - Chronological split-conformal 90% prediction intervals with calibration state and
45
+ scope, kept separate from race-performance `std_dev`.
46
+ - Safe checksummed JSON core artifact with its canonical species-property lookup,
47
+ packaged with the wheel and one immutable
48
+ `PredictionBundle` snapshot per request.
49
+ - Optional CatBoost residual artifact and strict rolling-origin promotion gate. No
50
+ residual candidate is promoted in the 2.0.0 release.
51
+ - Deterministic joint mark optimizer using 2,048 common-random samples, exhaustive
52
+ global search for tractable small fields, and at most eight coordinate passes for
53
+ larger fields, with a bounded rounded-gap fallback.
54
+ - Append-only local SQLite prediction ledger, authenticated REST ledger routes,
55
+ immutable settlement revisions, a replayable sanitized mirror outbox, and optional
56
+ best-effort Supabase mirroring through migrations
57
+ `20260811_005_prediction_v2.sql` and `20260813_006_prediction_hash_algorithm.sql`.
58
+ - Frozen release evidence and a verify-only operator command: `python train_model.py`.
59
+
60
+ ### Changed
61
+
62
+ - Numeric evidence is now restricted to stable identity/history, event, strictly prior
63
+ dated results, diameter, species physical properties, and gender including missing.
64
+ - `get_all_predictions()` maps `baseline` to the V2 core, `ml` to a promoted residual
65
+ only, `panel` to the broad fallback, and `llm` to `None`.
66
+ - `HandicapCalculator.calculate()` snapshots the engine once and chooses marks jointly.
67
+ - REST responses include interval, engine/model/calibration, warning, optimizer,
68
+ prediction-ID, degradation, and ledger-state fields.
69
+ - Public `/calculate` and `/predict` remain stateless. Trusted writes use
70
+ `/ledger/calculate` and require stable IDs, bearer authentication, and `request_id`.
71
+
72
+ ### Retired
73
+
74
+ - Numeric LLM generation, selection, and adjustment. LLMs remain available only for
75
+ narrative features.
76
+ - Numeric use of division, round/heat, venue, lane/stand, run order, exact material
77
+ identity, wood quality/moisture, weather, equipment, fatigue, penalty/DNF state,
78
+ same-tournament weighting, and field strength. Legacy inputs remain accepted as
79
+ documented no-ops for one migration release.
80
+
81
+ ### Validation
82
+
83
+ - Frozen 128-row temporal test: MAE 16.1301 vs 20.5172 for the strict prior-only
84
+ incumbent (21.38% lower); RMSE 33.6904 vs 44.4791 (24.26% lower); 90% interval
85
+ coverage 94.53%. These results apply only to the checked-in workbook and split;
86
+ cohort samples are smaller and no universal accuracy or fairness claim is made.
87
+
88
+ ### Migration
89
+
90
+ - Default engine is V2. Set `STRATHMARK_PREDICTION_ENGINE=legacy` for the temporary,
91
+ deterministic baseline-only rollback. The rollback never invokes an LLM numerically.
92
+ - See `docs/PREDICTION_ENGINE_V2.md` and `docs/DEPLOYMENT.md` before upgrading a live
93
+ consumer.
94
+
95
+ ## [1.0.0] - 2026-05-08
96
+
97
+ First public PyPI release. No API changes relative to 0.5.0; the 1.0.0 designation marks publication readiness, not a redesign. The library has been production-tested for multiple seasons across STRATHEX and the Missoula Pro-Am tournament manager.
98
+
99
+ ### Added
100
+ - PyPI distribution metadata: `Development Status :: 5 - Production/Stable`, `License :: OSI Approved :: Apache Software License`, `Operating System :: OS Independent`, and `Topic :: Software Development :: Libraries` classifiers.
101
+ - `[project.urls]` entries for Homepage, Issues, Documentation, and Changelog (in addition to the existing Source URL).
102
+ - Author email field in `[project.authors]`.
103
+ - Lower bound on the `ollama` optional dependency (`ollama>=0.3`) in the `[llm]` extra; previously unconstrained.
104
+
105
+ ### Changed
106
+ - Version bumped from 0.5.0 to 1.0.0 to mark the first stable PyPI release. `__version__` in `strathmark/__init__.py` updated to match.
107
+ - README image and inter-doc links rewritten as absolute GitHub URLs (`https://raw.githubusercontent.com/.../main/...` for the logo, `https://github.com/SquirmyWormy275/STRATHMARK/blob/main/...` and `.../tree/main/...` for documents and directories) so they render correctly on the PyPI project page.
108
+ - README test-count figure updated from 708 to 759 to match the current suite.
109
+ - `.gitignore` virtual-environment block extended with `.venv*/` so per-purpose venvs (e.g. `.venv-pypi-readiness`) are ignored without relying on Python's venv-internal `.gitignore`.
110
+
111
+ ### Fixed
112
+ - README "Usage" prose claimed `HandicapCalculator().calculate()` returns `CalculationResult` objects with a `gap` attribute. The actual return type is `MarkResult` (defined in `strathmark.calculator`) with fields `name`, `mark`, `predicted_time`, `method_used`, `confidence`, `explanation`, `std_dev`. README rewritten to match the real API. Discovered by running the README example against the built wheel.
113
+
114
+ ### Removed
115
+ - Placeholder LinkedIn line in the README "About the author" section.
116
+
117
+ ## [0.5.0] - 2026-05-08
118
+
119
+ ### Added
120
+ - MNEMEX integration: STRATHMARK Supabase becomes a hydrated cache of canonical results held in a separate MNEMEX project. Sync runs in dry-run mode until `MNEMEX_SUPABASE_URL`/`MNEMEX_SUPABASE_KEY` are configured.
121
+ - `strathmark/mnemex.py` — read-only MNEMEX client plus competitor registration helper. Reads canonical results, looks up canonical competitors, mints new ones via ULID.
122
+ - `strathmark/sync.py` — three sync paths sharing one upsert core: `nightly_batch()` (cron at 03:00 UTC), `strathex_finalization(event_id)` (webhook), `manual_force_sync(show_name=None, since=None)` (admin button / CLI). Failures raise after writing an audit-trail row to `sync_log`.
123
+ - `strathmark/drift.py` — calibration drift detection. `evaluate_drift(model_version_id, lookback_days=30)` returns a `DriftReport` with mean-shift, variance-ratio, and empirical-coverage alerts. Advisory only; never auto-deactivates a model.
124
+ - `strathmark/db.py` ML state writes: `register_model_version()`, `set_active_model()`, `record_calibration()`, `store_features()`, `record_prediction()`, `settle_prediction()`. Best-effort vs raise-on-failure discipline matches the policy doc.
125
+ - `_BiasCircuitBreaker` in `strathmark/predictor.py` — 60-second sliding window, 3-strike threshold, auto-reset. Replaces the prior session-level disable so transient Supabase blips don't permanently degrade bias correction.
126
+ - Three SQL migrations under `strathmark/migrations/`: `001` adds source-tracking columns (`source_type`, `mnemex_id`), `002` creates ML state tables (`model_versions`, `calibration_tables`, `feature_store`, `predictions`, `prediction_residuals` extensions), `003` reframes RLS for controlled-write enforcement (FORCE ROW LEVEL SECURITY plus a dedicated `mnemex_sync` role plumbing checklist).
127
+ - `scripts/rekey_against_mnemex.py` — idempotent re-keying script that fills in `mnemex_id` on existing STRATHMARK competitor rows from MNEMEX. Refuses to commit below the 95% match-rate threshold without `--force`.
128
+ - `docs/ml-persistence-policy.md` — policy doc covering retraining cadence, model versioning, calibration drift, feature store, hot-path circuit breaker, non-blocking guarantee.
129
+ - `docs/schema-reality-2026-05-04.md`, `docs/cleanup-candidates-2026-05-04.md`, `docs/ml-research-questions.md` — Phase 1 schema verification plus a cleanup audit confirming zero stray validation rows.
130
+ - 65 new tests across `tests/test_mnemex.py`, `tests/test_sync.py`, `tests/test_drift.py`, `tests/test_circuit_breaker.py`, `tests/test_ml_state.py`, plus 277 lines of additions to `tests/test_db.py`.
131
+
132
+ ### Changed
133
+ - `register_competitor()` rewrite: routes through MNEMEX when configured, falls back to STRATHMARK-local mint with a deprecation warning when MNEMEX is unset. Default `wait_for_sync=False`; opting in to blocking requires explicit kwarg.
134
+ - `get_competitor_bias()` no longer swallows DB exceptions internally. Callers on the prediction hot path wrap in `_BiasCircuitBreaker`; the policy is documented in `docs/ml-persistence-policy.md` section 5.
135
+ - `_do_sync` writes a failure sync_log row before re-raising on Supabase upsert error, so the audit trail is preserved AND callers see a non-zero exit (matches the module docstring).
136
+ - Added `ulid-py>=1.1` to base dependencies.
137
+
138
+ ### Fixed
139
+ - Cascade naming mismatch: `record_prediction` validator and the `predictions_cascade_level_check` constraint now accept `'panel'` instead of `'panel_fallback'`, matching the canonical emitter strings in `strathmark/predictor.py`.
140
+ - Drift detection's coverage rule now compares empirical coverage of recent residuals against a 90% prediction interval derived from baseline residual quantiles, replacing a check that compared the static calibration-time `coverage_at_90` value against a fixed band.
141
+ - RLS migration `003` now adds `FORCE ROW LEVEL SECURITY` to all governed tables, closing the BYPASSRLS bypass on `service_role`. Pre-application checklist expanded to require role plumbing AND `NOBYPASSRLS` rotation. The `wood_species_write_admin` policy no longer ORs in `service_role` (contradicted the header's "wood_admin only" intent).
142
+
143
+ ## [0.4.1] - 2026-04-21
144
+
145
+ ### Added
146
+ - `ONBOARDING.md` at repo root — routing hub for new contributors by task (bug fix, feature, deployment debug, AI agent)
147
+ - 7 new learning docs in `docs/solutions/`:
148
+ - `best-practices/llm-cascade-and-monte-carlo-tuning-2026-04-21.md` — cross-cutting v0.3.0 hardening narrative
149
+ - `best-practices/optional-dependency-gating-importorskip.md` — `[extras]` split + `pytest.importorskip` placement gotcha
150
+ - `best-practices/config-env-vars-importlib-reload-pattern.md` — test pattern for import-time env vars
151
+ - `architecture-decisions/dual-store-sqlite-supabase-split.md` — offline-first rationale for SQLite + Supabase
152
+ - `architecture-decisions/ensemble-predictor-design-decisions.md` — accepted design + deferred scope for TODO-001..011
153
+ - `test-failures/regression-test-must-include-triggering-input.md` — Codex-caught regression-coverage pattern
154
+ - `workflow-patterns/eng-review-cycle-and-todos-handoff.md` — plan-review → TODOS.md → impl → Codex → ship pattern
155
+
156
+ ### Changed
157
+ - `docs/solutions/architecture-decisions/prediction-cascade-order.md` — added v0.3.0 provenance for JSON-schema / temp=0 / quality==5, cross-link to new hardening doc, `last_updated: 2026-04-21`
158
+ - `docs/solutions/architecture-decisions/mark-floor-ceiling-invariants.md` — added pre-v0.3.0 ceiling-rounding historical note, cross-link to new hardening doc, `last_updated: 2026-04-21`
159
+ - `strathmark/__init__.py` — `__version__` corrected from `0.3.1` (stale) to `0.4.1`
160
+ - `README.md` — version string updated to 0.4.1 and onboarding pointer added
161
+
162
+ ## [0.4.0] - 2026-04-07
163
+
164
+ ### Added
165
+ - Gemini cloud fallback (`google-generativeai`) in `llm.py` as a race-day fail-through when Ollama is unreachable
166
+ - `_env_int`/`_env_str` helpers in `config.py` for safe env-var parsing with sensible fallbacks
167
+ - `scripts/validate_deployment.py` — pre-event read-only deployment validation
168
+ - `scripts/ingest_proam_results.py` — post-event Pro-Am result ingestion with dry-run / --commit modes
169
+ - `push_results_dicts()` and `register_competitor()` public helpers for programmatic ingestion
170
+ - `docs/DEPLOYMENT.md` — full deployment guide
171
+ - `tests/test_deployment_fallbacks.py` — fail-fast cascade tests for unreachable Ollama / Supabase
172
+
173
+ ### Changed
174
+ - Race-day Ollama defaults: `TIMEOUT_SECONDS` and `MAX_RETRIES` now env-overridable via `STRATHMARK_OLLAMA_*`; default `MAX_RETRIES=0` for fail-fast cascade behavior
175
+ - `STRATHMARK_OLLAMA_URL` / `STRATHMARK_OLLAMA_TIMEOUT` / `STRATHMARK_OLLAMA_MAX_RETRIES` now documented as first-class operational knobs
176
+
177
+ ### Fixed
178
+ - Ollama cascade hang on unreachable host (120s blocking per call) — fail-fast via reduced timeout + zero retries
179
+ - Ollama status cache race condition under concurrent FastAPI requests — `threading.Lock` protecting cache reads/writes
180
+ - `LLMConfig` env-var resolution now happens at call time (via helpers) for hot-swappable values, while import-time frozen dataclass is retained for stable params
181
+
182
+ ## [0.3.1] - 2026-03-24
183
+
184
+ ### Added
185
+ - 221 new tests across 9 test files, bringing total from 446 to 667
186
+ - Regression tests for fixed bugs: banker's rounding, decay weights, timeout filtering, absolute variance, mark floor/ceiling enforcement
187
+ - Boundary tests for extreme values: 50-competitor fields, diameters 50-600mm, quality clamping, consistency rating thresholds
188
+ - Config invariant tests: frozen dataclass enforcement, rules consistency, threshold ordering, ML/LLM config sanity
189
+ - Extended decay tests: exponential precision, date type handling, adaptive vs fixed weighting, robust MAD clipping
190
+ - Extended store tests: CRUD, duplicate detection, DataFrame round-trips, column aliases, date handling
191
+ - Extended visualization tests: ASCII bar chart accuracy, line width, percentage sums, large field rendering
192
+ - Predictor regression tests: cascade priority, tournament weighting with num_tournament_rounds, division fallback
193
+ - Full pipeline integration tests: predict-calculate-simulate round-trips, multi-event days, store round-trips, simulation determinism
194
+ - Wood boundary tests: species multipliers, Janka hardness monotonicity, event scaling exponents
@@ -0,0 +1,64 @@
1
+ # Contributing to STRATHMARK
2
+
3
+ STRATHMARK is the shared handicap calculation core for STRATHEX,
4
+ Missoula-Pro-Am-Manager, and any future tournament manager built on the
5
+ same engine. Changes here propagate downstream, so the bar for tests and
6
+ lint is intentionally strict.
7
+
8
+ ## Development install
9
+
10
+ ```bash
11
+ pip install -e ".[dev]"
12
+ ```
13
+
14
+ Optional extras you may also want while working on specific subsystems:
15
+
16
+ - `pip install -e ".[ml]"` — XGBoost / LightGBM / scikit-learn predictor
17
+ - `pip install -e ".[db]"` — Supabase backend
18
+ - `pip install -e ".[api]"` — FastAPI server
19
+ - `pip install -e ".[llm]"` — optional narrative-analysis client
20
+
21
+ ## Running the test suite
22
+
23
+ ```bash
24
+ pytest tests/ -v
25
+ ```
26
+
27
+ Supabase-backed db tests are gated behind `STRATHMARK_TEST_DB=1` and skip
28
+ by default. ML predictor paths fall back to baseline when the `[ml]` extras
29
+ are not installed, so you do not need them to get a green run. Install
30
+ `.[dev,api]` to run the REST API contract tests locally.
31
+
32
+ ## Linting and formatting
33
+
34
+ ```bash
35
+ ruff check .
36
+ ruff format --check .
37
+ ```
38
+
39
+ To auto-fix and apply formatting:
40
+
41
+ ```bash
42
+ ruff check . --fix
43
+ ruff format .
44
+ ```
45
+
46
+ ## CI
47
+
48
+ GitHub Actions runs lint, the full test matrix (Ubuntu + Windows across
49
+ Python 3.10 through 3.13), oldest/current API dependency contracts, release and
50
+ cross-platform golden verification, the optimizer capacity gate, and isolated wheel
51
+ and source-distribution installs. **All PRs must pass CI before merge.** The workflow
52
+ definitions live under [`.github/workflows/`](.github/workflows/).
53
+
54
+ ## Design rules (non-negotiable)
55
+
56
+ These are enforced in code and in review. See the README's *Design rules*
57
+ section for the full list, but the load-bearing ones are:
58
+
59
+ - Mark floor: 3 seconds
60
+ - Mark ceiling: 183 seconds system-wide
61
+ - Variance: performance uncertainty is represented in absolute seconds; proportional
62
+ scaling is only a clamped no-history fallback
63
+ - Prediction engine: manual override, otherwise the validated V2 posterior with deterministic fallback
64
+ - Output: plain text only, no emojis, no ANSI color codes