loadcoach 1.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 (231) hide show
  1. loadcoach-1.0.0/.editorconfig +23 -0
  2. loadcoach-1.0.0/.github/workflows/ci.yml +174 -0
  3. loadcoach-1.0.0/.github/workflows/release.yml +63 -0
  4. loadcoach-1.0.0/.gitignore +100 -0
  5. loadcoach-1.0.0/.importlinter +40 -0
  6. loadcoach-1.0.0/.pre-commit-config.yaml +22 -0
  7. loadcoach-1.0.0/CHANGELOG.md +635 -0
  8. loadcoach-1.0.0/CONTRIBUTING.md +50 -0
  9. loadcoach-1.0.0/LICENSE +201 -0
  10. loadcoach-1.0.0/PKG-INFO +120 -0
  11. loadcoach-1.0.0/README.md +75 -0
  12. loadcoach-1.0.0/SECURITY.md +39 -0
  13. loadcoach-1.0.0/docs/README.md +16 -0
  14. loadcoach-1.0.0/docs/apps/loadcoach/api.md +305 -0
  15. loadcoach-1.0.0/docs/apps/loadcoach/data-model.md +281 -0
  16. loadcoach-1.0.0/docs/apps/loadcoach/development-plan.md +484 -0
  17. loadcoach-1.0.0/docs/apps/loadcoach/queue-and-scheduling.md +322 -0
  18. loadcoach-1.0.0/docs/apps/loadcoach/risks.md +105 -0
  19. loadcoach-1.0.0/docs/apps/loadcoach/routing.md +341 -0
  20. loadcoach-1.0.0/docs/apps/loadcoach/spec.md +387 -0
  21. loadcoach-1.0.0/docs/configuration.md +158 -0
  22. loadcoach-1.0.0/docs/openapi.json +1984 -0
  23. loadcoach-1.0.0/docs/operations.md +99 -0
  24. loadcoach-1.0.0/docs/quickstart.md +91 -0
  25. loadcoach-1.0.0/docs/routing.md +64 -0
  26. loadcoach-1.0.0/docs/security.md +104 -0
  27. loadcoach-1.0.0/docs/troubleshooting.md +65 -0
  28. loadcoach-1.0.0/docs/upgrading.md +52 -0
  29. loadcoach-1.0.0/pyproject.toml +127 -0
  30. loadcoach-1.0.0/requirements/.gitkeep +1 -0
  31. loadcoach-1.0.0/requirements/README.md +76 -0
  32. loadcoach-1.0.0/requirements/ci.lock +1521 -0
  33. loadcoach-1.0.0/requirements/release.in +6 -0
  34. loadcoach-1.0.0/requirements/release.lock +493 -0
  35. loadcoach-1.0.0/src/loadcoach/__about__.py +1 -0
  36. loadcoach-1.0.0/src/loadcoach/__main__.py +12 -0
  37. loadcoach-1.0.0/src/loadcoach/bootstrap.py +157 -0
  38. loadcoach-1.0.0/src/loadcoach/cli/commands/config.py +169 -0
  39. loadcoach-1.0.0/src/loadcoach/cli/commands/db.py +249 -0
  40. loadcoach-1.0.0/src/loadcoach/cli/commands/evidence.py +347 -0
  41. loadcoach-1.0.0/src/loadcoach/cli/commands/generate.py +134 -0
  42. loadcoach-1.0.0/src/loadcoach/cli/commands/job.py +394 -0
  43. loadcoach-1.0.0/src/loadcoach/cli/commands/models.py +215 -0
  44. loadcoach-1.0.0/src/loadcoach/cli/commands/queue.py +162 -0
  45. loadcoach-1.0.0/src/loadcoach/cli/commands/reliability.py +92 -0
  46. loadcoach-1.0.0/src/loadcoach/cli/commands/route.py +191 -0
  47. loadcoach-1.0.0/src/loadcoach/cli/commands/system.py +137 -0
  48. loadcoach-1.0.0/src/loadcoach/cli/commands/tasks.py +158 -0
  49. loadcoach-1.0.0/src/loadcoach/cli/commands/token.py +146 -0
  50. loadcoach-1.0.0/src/loadcoach/cli/main.py +84 -0
  51. loadcoach-1.0.0/src/loadcoach/config/manual_capability_scores.toml +15 -0
  52. loadcoach-1.0.0/src/loadcoach/config/schemas/code_debug_findings.json +13 -0
  53. loadcoach-1.0.0/src/loadcoach/config/schemas/code_review_findings.json +25 -0
  54. loadcoach-1.0.0/src/loadcoach/config/schemas/content_review_findings.json +24 -0
  55. loadcoach-1.0.0/src/loadcoach/config/schemas/fact_check_findings.json +26 -0
  56. loadcoach-1.0.0/src/loadcoach/config/schemas/structured_extract.json +11 -0
  57. loadcoach-1.0.0/src/loadcoach/config/task_profiles.toml +417 -0
  58. loadcoach-1.0.0/src/loadcoach/config.py +894 -0
  59. loadcoach-1.0.0/src/loadcoach/domain/admission.py +230 -0
  60. loadcoach-1.0.0/src/loadcoach/domain/authorization.py +105 -0
  61. loadcoach-1.0.0/src/loadcoach/domain/circuit_breaker.py +357 -0
  62. loadcoach-1.0.0/src/loadcoach/domain/evidence_policy.py +848 -0
  63. loadcoach-1.0.0/src/loadcoach/domain/priority.py +158 -0
  64. loadcoach-1.0.0/src/loadcoach/domain/queue_state.py +214 -0
  65. loadcoach-1.0.0/src/loadcoach/domain/registry.py +194 -0
  66. loadcoach-1.0.0/src/loadcoach/domain/reliability.py +812 -0
  67. loadcoach-1.0.0/src/loadcoach/domain/retry_policy.py +192 -0
  68. loadcoach-1.0.0/src/loadcoach/domain/routing/constraints.py +567 -0
  69. loadcoach-1.0.0/src/loadcoach/domain/routing/context_budget.py +188 -0
  70. loadcoach-1.0.0/src/loadcoach/domain/routing/explanation.py +288 -0
  71. loadcoach-1.0.0/src/loadcoach/domain/routing/narrative.py +360 -0
  72. loadcoach-1.0.0/src/loadcoach/domain/routing/ranking.py +118 -0
  73. loadcoach-1.0.0/src/loadcoach/domain/routing/scoring.py +549 -0
  74. loadcoach-1.0.0/src/loadcoach/domain/routing/subject.py +343 -0
  75. loadcoach-1.0.0/src/loadcoach/domain/task_profile.py +221 -0
  76. loadcoach-1.0.0/src/loadcoach/domain/validation.py +399 -0
  77. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/.gitkeep +0 -0
  78. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/env.py +34 -0
  79. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/script.py.mako +26 -0
  80. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/versions/0001_initial_schema.py +161 -0
  81. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/versions/0002_routing_decisions.py +118 -0
  82. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/versions/0003_jobs_and_attempts.py +218 -0
  83. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/versions/0004_residency.py +84 -0
  84. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/versions/0005_capability_evidence.py +143 -0
  85. loadcoach-1.0.0/src/loadcoach/infrastructure/db/migrations/versions/0006_feedback_and_reliability.py +111 -0
  86. loadcoach-1.0.0/src/loadcoach/infrastructure/db/models.py +727 -0
  87. loadcoach-1.0.0/src/loadcoach/infrastructure/db/repositories/.gitkeep +0 -0
  88. loadcoach-1.0.0/src/loadcoach/infrastructure/db/repositories/settings.py +51 -0
  89. loadcoach-1.0.0/src/loadcoach/infrastructure/freeweight_client.py +470 -0
  90. loadcoach-1.0.0/src/loadcoach/infrastructure/providers/factory.py +63 -0
  91. loadcoach-1.0.0/src/loadcoach/observability/logging.py +144 -0
  92. loadcoach-1.0.0/src/loadcoach/prompts/.gitkeep +0 -0
  93. loadcoach-1.0.0/src/loadcoach/prompts/execution/structured_output.retry.v1.json +38 -0
  94. loadcoach-1.0.0/src/loadcoach/prompts/manifest.json +14 -0
  95. loadcoach-1.0.0/src/loadcoach/services/config_reference.py +154 -0
  96. loadcoach-1.0.0/src/loadcoach/services/dashboard.py +191 -0
  97. loadcoach-1.0.0/src/loadcoach/services/database.py +359 -0
  98. loadcoach-1.0.0/src/loadcoach/services/doctor.py +535 -0
  99. loadcoach-1.0.0/src/loadcoach/services/evidence.py +1563 -0
  100. loadcoach-1.0.0/src/loadcoach/services/execution.py +1589 -0
  101. loadcoach-1.0.0/src/loadcoach/services/feedback.py +245 -0
  102. loadcoach-1.0.0/src/loadcoach/services/health.py +379 -0
  103. loadcoach-1.0.0/src/loadcoach/services/job_events.py +287 -0
  104. loadcoach-1.0.0/src/loadcoach/services/machine.py +73 -0
  105. loadcoach-1.0.0/src/loadcoach/services/models.py +485 -0
  106. loadcoach-1.0.0/src/loadcoach/services/prompts.py +63 -0
  107. loadcoach-1.0.0/src/loadcoach/services/queue.py +1501 -0
  108. loadcoach-1.0.0/src/loadcoach/services/queue_stream.py +151 -0
  109. loadcoach-1.0.0/src/loadcoach/services/recovery.py +165 -0
  110. loadcoach-1.0.0/src/loadcoach/services/reliability.py +450 -0
  111. loadcoach-1.0.0/src/loadcoach/services/residency.py +367 -0
  112. loadcoach-1.0.0/src/loadcoach/services/retention.py +126 -0
  113. loadcoach-1.0.0/src/loadcoach/services/routing.py +903 -0
  114. loadcoach-1.0.0/src/loadcoach/services/settings.py +271 -0
  115. loadcoach-1.0.0/src/loadcoach/services/status.py +125 -0
  116. loadcoach-1.0.0/src/loadcoach/services/task_profiles.py +140 -0
  117. loadcoach-1.0.0/src/loadcoach/services/telemetry_stream.py +170 -0
  118. loadcoach-1.0.0/src/loadcoach/services/tokens.py +164 -0
  119. loadcoach-1.0.0/src/loadcoach/services/worker.py +1920 -0
  120. loadcoach-1.0.0/src/loadcoach/web/app.py +404 -0
  121. loadcoach-1.0.0/src/loadcoach/web/auth.py +189 -0
  122. loadcoach-1.0.0/src/loadcoach/web/csrf.py +55 -0
  123. loadcoach-1.0.0/src/loadcoach/web/limits.py +149 -0
  124. loadcoach-1.0.0/src/loadcoach/web/rate_limit.py +282 -0
  125. loadcoach-1.0.0/src/loadcoach/web/rendering.py +79 -0
  126. loadcoach-1.0.0/src/loadcoach/web/routes/access.py +44 -0
  127. loadcoach-1.0.0/src/loadcoach/web/routes/dashboard.py +36 -0
  128. loadcoach-1.0.0/src/loadcoach/web/routes/evidence.py +263 -0
  129. loadcoach-1.0.0/src/loadcoach/web/routes/generate.py +372 -0
  130. loadcoach-1.0.0/src/loadcoach/web/routes/jobs.py +411 -0
  131. loadcoach-1.0.0/src/loadcoach/web/routes/models.py +153 -0
  132. loadcoach-1.0.0/src/loadcoach/web/routes/queue.py +181 -0
  133. loadcoach-1.0.0/src/loadcoach/web/routes/reliability.py +103 -0
  134. loadcoach-1.0.0/src/loadcoach/web/routes/routing.py +203 -0
  135. loadcoach-1.0.0/src/loadcoach/web/routes/settings.py +106 -0
  136. loadcoach-1.0.0/src/loadcoach/web/routes/system.py +110 -0
  137. loadcoach-1.0.0/src/loadcoach/web/routes/task_profiles.py +59 -0
  138. loadcoach-1.0.0/src/loadcoach/web/routing_support.py +87 -0
  139. loadcoach-1.0.0/src/loadcoach/web/templates/.gitkeep +0 -0
  140. loadcoach-1.0.0/src/loadcoach/web/templates/dashboard/index.html +89 -0
  141. loadcoach-1.0.0/src/loadcoach/web/templates/error.html +34 -0
  142. loadcoach-1.0.0/src/loadcoach/web/templates/evidence/index.html +105 -0
  143. loadcoach-1.0.0/src/loadcoach/web/templates/jobs/detail.html +100 -0
  144. loadcoach-1.0.0/src/loadcoach/web/templates/jobs/index.html +46 -0
  145. loadcoach-1.0.0/src/loadcoach/web/templates/models/index.html +42 -0
  146. loadcoach-1.0.0/src/loadcoach/web/templates/queue/_live.html +63 -0
  147. loadcoach-1.0.0/src/loadcoach/web/templates/queue/index.html +47 -0
  148. loadcoach-1.0.0/src/loadcoach/web/templates/reliability/index.html +106 -0
  149. loadcoach-1.0.0/src/loadcoach/web/templates/routing/_narrative.html +67 -0
  150. loadcoach-1.0.0/src/loadcoach/web/templates/routing/detail.html +85 -0
  151. loadcoach-1.0.0/src/loadcoach/web/templates/routing/index.html +30 -0
  152. loadcoach-1.0.0/src/loadcoach/web/templates/settings/index.html +39 -0
  153. loadcoach-1.0.0/src/loadcoach/web/templates/system/index.html +84 -0
  154. loadcoach-1.0.0/src/loadcoach/web/templates/task_profiles/index.html +24 -0
  155. loadcoach-1.0.0/tests/accessibility/test_ui_checklist.py +270 -0
  156. loadcoach-1.0.0/tests/conftest.py +128 -0
  157. loadcoach-1.0.0/tests/contract/test_evidence_import.py +201 -0
  158. loadcoach-1.0.0/tests/contract/test_openapi_snapshot.py +68 -0
  159. loadcoach-1.0.0/tests/contract/test_schema_rejection.py +185 -0
  160. loadcoach-1.0.0/tests/e2e/test_dashboard.py +137 -0
  161. loadcoach-1.0.0/tests/e2e/test_job_detail.py +159 -0
  162. loadcoach-1.0.0/tests/e2e/test_model_and_profile_detail.py +55 -0
  163. loadcoach-1.0.0/tests/e2e/test_models_and_task_profiles.py +99 -0
  164. loadcoach-1.0.0/tests/e2e/test_queue_controls.py +100 -0
  165. loadcoach-1.0.0/tests/e2e/test_server_boot.py +120 -0
  166. loadcoach-1.0.0/tests/e2e/test_settings.py +119 -0
  167. loadcoach-1.0.0/tests/e2e/test_system_page.py +41 -0
  168. loadcoach-1.0.0/tests/integration/_other_app_fixture/env.py +35 -0
  169. loadcoach-1.0.0/tests/integration/_other_app_fixture/models.py +29 -0
  170. loadcoach-1.0.0/tests/integration/_other_app_fixture/versions/0001_create_machines.py +29 -0
  171. loadcoach-1.0.0/tests/integration/test_breaker_probe.py +207 -0
  172. loadcoach-1.0.0/tests/integration/test_breaker_sync_path.py +135 -0
  173. loadcoach-1.0.0/tests/integration/test_cancellation.py +88 -0
  174. loadcoach-1.0.0/tests/integration/test_discovery.py +165 -0
  175. loadcoach-1.0.0/tests/integration/test_evidence_api.py +453 -0
  176. loadcoach-1.0.0/tests/integration/test_evidence_fetch.py +638 -0
  177. loadcoach-1.0.0/tests/integration/test_evidence_importer.py +603 -0
  178. loadcoach-1.0.0/tests/integration/test_evidence_routing_change.py +635 -0
  179. loadcoach-1.0.0/tests/integration/test_feedback_affects_routing.py +586 -0
  180. loadcoach-1.0.0/tests/integration/test_feedback_api.py +240 -0
  181. loadcoach-1.0.0/tests/integration/test_generate.py +405 -0
  182. loadcoach-1.0.0/tests/integration/test_jobs_api.py +275 -0
  183. loadcoach-1.0.0/tests/integration/test_migrations.py +582 -0
  184. loadcoach-1.0.0/tests/integration/test_queue.py +586 -0
  185. loadcoach-1.0.0/tests/integration/test_queue_stream.py +188 -0
  186. loadcoach-1.0.0/tests/integration/test_recovery.py +232 -0
  187. loadcoach-1.0.0/tests/integration/test_reliability_api.py +164 -0
  188. loadcoach-1.0.0/tests/integration/test_retention.py +131 -0
  189. loadcoach-1.0.0/tests/integration/test_route_endpoint.py +487 -0
  190. loadcoach-1.0.0/tests/integration/test_streaming.py +326 -0
  191. loadcoach-1.0.0/tests/integration/test_validation.py +300 -0
  192. loadcoach-1.0.0/tests/integration/test_worker.py +103 -0
  193. loadcoach-1.0.0/tests/live/test_ollama_generation.py +115 -0
  194. loadcoach-1.0.0/tests/performance/.gitkeep +0 -0
  195. loadcoach-1.0.0/tests/performance/test_generate_overhead.py +110 -0
  196. loadcoach-1.0.0/tests/performance/test_queue_budgets.py +288 -0
  197. loadcoach-1.0.0/tests/performance/test_routing_budgets.py +227 -0
  198. loadcoach-1.0.0/tests/performance/test_streaming_gap.py +105 -0
  199. loadcoach-1.0.0/tests/security/.gitkeep +0 -0
  200. loadcoach-1.0.0/tests/security/test_checklist.py +427 -0
  201. loadcoach-1.0.0/tests/security/test_queue_cap.py +38 -0
  202. loadcoach-1.0.0/tests/security/test_rate_limit.py +201 -0
  203. loadcoach-1.0.0/tests/security/test_scopes.py +472 -0
  204. loadcoach-1.0.0/tests/simulation/simulator.py +1015 -0
  205. loadcoach-1.0.0/tests/simulation/test_scale.py +110 -0
  206. loadcoach-1.0.0/tests/simulation/test_scheduling_properties.py +1176 -0
  207. loadcoach-1.0.0/tests/simulation/test_simulator_mechanics.py +341 -0
  208. loadcoach-1.0.0/tests/unit/test_admission.py +122 -0
  209. loadcoach-1.0.0/tests/unit/test_circuit_breaker.py +138 -0
  210. loadcoach-1.0.0/tests/unit/test_cli.py +355 -0
  211. loadcoach-1.0.0/tests/unit/test_config.py +180 -0
  212. loadcoach-1.0.0/tests/unit/test_config_reference.py +53 -0
  213. loadcoach-1.0.0/tests/unit/test_doctor.py +174 -0
  214. loadcoach-1.0.0/tests/unit/test_evidence_policy.py +504 -0
  215. loadcoach-1.0.0/tests/unit/test_health.py +162 -0
  216. loadcoach-1.0.0/tests/unit/test_priority.py +113 -0
  217. loadcoach-1.0.0/tests/unit/test_queue_state.py +142 -0
  218. loadcoach-1.0.0/tests/unit/test_registry.py +127 -0
  219. loadcoach-1.0.0/tests/unit/test_reliability_math.py +586 -0
  220. loadcoach-1.0.0/tests/unit/test_residency_service.py +139 -0
  221. loadcoach-1.0.0/tests/unit/test_retry_policy.py +102 -0
  222. loadcoach-1.0.0/tests/unit/test_routing_constraints.py +425 -0
  223. loadcoach-1.0.0/tests/unit/test_routing_context_budget.py +115 -0
  224. loadcoach-1.0.0/tests/unit/test_routing_explanation.py +170 -0
  225. loadcoach-1.0.0/tests/unit/test_routing_narrative.py +222 -0
  226. loadcoach-1.0.0/tests/unit/test_routing_ranking.py +161 -0
  227. loadcoach-1.0.0/tests/unit/test_routing_scoring.py +285 -0
  228. loadcoach-1.0.0/tests/unit/test_routing_subject.py +110 -0
  229. loadcoach-1.0.0/tests/unit/test_task_profile_validation.py +125 -0
  230. loadcoach-1.0.0/tests/unit/test_telemetry_stream.py +100 -0
  231. loadcoach-1.0.0/tests/unit/test_tokens.py +116 -0
@@ -0,0 +1,23 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.py]
12
+ indent_size = 4
13
+ max_line_length = 100
14
+
15
+ [*.{toml,yml,yaml,json}]
16
+ indent_size = 2
17
+
18
+ [*.md]
19
+ trim_trailing_whitespace = false
20
+ max_line_length = off
21
+
22
+ [Makefile]
23
+ indent_style = tab
@@ -0,0 +1,174 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ format:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with: { python-version: "3.12" }
15
+ - run: pip install ruff
16
+ - run: ruff format --check .
17
+
18
+ lint:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with: { python-version: "3.12" }
24
+ - run: pip install ruff
25
+ - run: ruff check .
26
+
27
+ # Every job below installs from the hash-pinned lock and then the package itself with
28
+ # `--no-deps` (Packaging Standards §4): what CI tests is what `requirements/ci.lock` resolved,
29
+ # not today's resolution, and the package is installed non-editably so coverage and imports
30
+ # measure the installed distribution. The 3.14 early-warning job is the one deliberate
31
+ # exception — see its comment.
32
+ types:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: actions/setup-python@v5
37
+ with: { python-version: "3.12" }
38
+ - run: pip install --require-hashes -r requirements/ci.lock
39
+ - run: pip install . --no-deps
40
+ - run: mypy src tests
41
+
42
+ boundaries:
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+ - uses: actions/setup-python@v5
47
+ with: { python-version: "3.12" }
48
+ - run: pip install --require-hashes -r requirements/ci.lock
49
+ - run: pip install . --no-deps
50
+ - run: lint-imports
51
+
52
+ tests:
53
+ runs-on: ubuntu-latest
54
+ strategy:
55
+ matrix:
56
+ python-version: ["3.12", "3.13"]
57
+ steps:
58
+ - uses: actions/checkout@v4
59
+ - uses: actions/setup-python@v5
60
+ with: { python-version: "${{ matrix.python-version }}" }
61
+ - run: pip install --require-hashes -r requirements/ci.lock
62
+ - run: pip install . --no-deps
63
+ - run: pytest -m "not live and not performance" --cov --cov-report=xml
64
+
65
+ tests-314-early-warning:
66
+ runs-on: ubuntu-latest
67
+ continue-on-error: true
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+ - uses: actions/setup-python@v5
71
+ with: { python-version: "3.14" }
72
+ # Deliberately unpinned: ci.lock is resolved on 3.13, and pinning a version that has no
73
+ # 3.14 wheels would defeat the purpose of an early warning.
74
+ - run: pip install -e ".[dev]"
75
+ - run: pytest -m "not live and not performance"
76
+
77
+ db-matrix:
78
+ name: tests (PostgreSQL)
79
+ runs-on: ubuntu-latest
80
+ services:
81
+ postgres:
82
+ image: postgres:16
83
+ # Credentials and database name match `weightsdb.testing`'s documented default URL, so the
84
+ # service this job starts is the one the package looks for. The previous values named a
85
+ # `postgres`/`postgres` server nothing ever connected to: `temporary_postgres` reads
86
+ # WEIGHTSDB_POSTGRES_URL, not DATABASE_URL, and under REQUIRE_POSTGRES=1 the unused
87
+ # default was a hard failure rather than a skip — so this job had never run a query.
88
+ env:
89
+ POSTGRES_USER: weightsdb
90
+ POSTGRES_PASSWORD: weightsdb
91
+ POSTGRES_DB: weightsdb_test
92
+ ports: ["5432:5432"]
93
+ options: >-
94
+ --health-cmd "pg_isready -U weightsdb -d weightsdb_test" --health-interval 5s --health-timeout 5s --health-retries 10
95
+ steps:
96
+ - uses: actions/checkout@v4
97
+ - uses: actions/setup-python@v5
98
+ with: { python-version: "3.12" }
99
+ - run: pip install --require-hashes -r requirements/ci.lock
100
+ - run: pip install . --no-deps
101
+ - run: pytest -m "not live and not performance" tests/integration
102
+ env:
103
+ WEIGHTSDB_REQUIRE_POSTGRES: "1"
104
+ # Set explicitly rather than relying on the default, so the job states which server it
105
+ # is testing.
106
+ WEIGHTSDB_POSTGRES_URL: postgresql+psycopg://weightsdb:weightsdb@localhost:5432/weightsdb_test
107
+
108
+ coverage:
109
+ needs: [tests]
110
+ runs-on: ubuntu-latest
111
+ steps:
112
+ - uses: actions/checkout@v4
113
+ - uses: actions/setup-python@v5
114
+ with: { python-version: "3.12" }
115
+ - run: pip install --require-hashes -r requirements/ci.lock
116
+ - run: pip install . --no-deps
117
+ - run: pytest -m "not live and not performance" --cov --cov-report=term-missing --cov-fail-under=85
118
+
119
+ contracts:
120
+ runs-on: ubuntu-latest
121
+ steps:
122
+ - uses: actions/checkout@v4
123
+ - uses: actions/setup-python@v5
124
+ with: { python-version: "3.12" }
125
+ - run: pip install --require-hashes -r requirements/ci.lock
126
+ - run: pip install . --no-deps
127
+ - run: pytest -m contract
128
+
129
+ security:
130
+ runs-on: ubuntu-latest
131
+ steps:
132
+ - uses: actions/checkout@v4
133
+ # gitleaks scans *history*, and `actions/checkout` fetches a single commit by default.
134
+ # For a push it is handed `<first-pushed>^..<last-pushed>`, so the parent of the first
135
+ # pushed commit has to be in the object store; in a depth-1 clone it is not, and git
136
+ # answers "unknown revision", which the action reports as exit code 1. It fails the same
137
+ # way whether or not a secret exists, so a green run would not have meant anything either.
138
+ with: { fetch-depth: 0 }
139
+ - uses: actions/setup-python@v5
140
+ with: { python-version: "3.12" }
141
+ - run: pip install pip-audit
142
+ # Audit the locked sets, not the job's own environment: a bare `pip-audit` here would
143
+ # inspect an environment containing only pip-audit itself (Security Standards §11).
144
+ - run: pip-audit --require-hashes -r requirements/ci.lock
145
+ - run: pip-audit --require-hashes -r requirements/release.lock
146
+ - uses: gitleaks/gitleaks-action@v2
147
+ env:
148
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
149
+
150
+ build:
151
+ runs-on: ubuntu-latest
152
+ steps:
153
+ - uses: actions/checkout@v4
154
+ - uses: actions/setup-python@v5
155
+ with: { python-version: "3.12" }
156
+ # The pinned build chain, byte-for-byte what `release.yml` uses, so a release is the
157
+ # artifact CI proved rather than a fresh resolution of `build` and `hatchling`.
158
+ - run: pip install --require-hashes -r requirements/release.lock
159
+ - run: python -m build --no-isolation
160
+ - run: twine check dist/*
161
+ - uses: actions/upload-artifact@v4
162
+ with: { name: dist, path: dist/ }
163
+
164
+ install-check:
165
+ needs: [build]
166
+ runs-on: ubuntu-latest
167
+ steps:
168
+ - uses: actions/checkout@v4
169
+ - uses: actions/setup-python@v5
170
+ with: { python-version: "3.12" }
171
+ - uses: actions/download-artifact@v4
172
+ with: { name: dist, path: dist/ }
173
+ - run: pip install dist/*.whl
174
+ - run: python -c "import loadcoach"
@@ -0,0 +1,63 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*.*.*"]
6
+ workflow_dispatch: # manual TestPyPI dry run; see publish-testpypi below
7
+
8
+ permissions:
9
+ id-token: write # required for PyPI Trusted Publishing
10
+ contents: write # required to create the GitHub release
11
+
12
+ jobs:
13
+ release:
14
+ # Tag pushes only. Without this, clicking "Run workflow" for the TestPyPI dry run below would
15
+ # also fire this job and publish to real PyPI — the exact opposite of a dry run.
16
+ if: github.event_name == 'push'
17
+ runs-on: ubuntu-latest
18
+ environment: pypi # must match the Environment name set on the PyPI trusted publisher
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-python@v5
22
+ with: { python-version: "3.12" }
23
+ # Byte-for-byte the dry run's build chain below. A real release that resolved `build` and
24
+ # `hatchling` fresh from PyPI would not be the artifact the dry run proved.
25
+ - run: pip install --require-hashes -r requirements/release.lock
26
+ - run: python -m build --no-isolation
27
+ - run: twine check dist/*
28
+ # NOTE: this step cannot succeed until `weightsdb 0.2.0` and `mirrorwall 0.2.0` are
29
+ # published — both are runtime dependencies of this package and neither is on any
30
+ # index today. See requirements/README.md.
31
+ - run: pip install "dist/$(ls dist | grep .whl)[dev]"
32
+ - run: pytest -m "not live and not performance"
33
+ - name: Publish to PyPI
34
+ uses: pypa/gh-action-pypi-publish@release/v1
35
+ - name: Create GitHub release
36
+ uses: softprops/action-gh-release@v2
37
+ with:
38
+ generate_release_notes: true
39
+ files: dist/*
40
+
41
+ publish-testpypi:
42
+ # Manual only, via Actions -> Release -> Run workflow. Packaging and Release Standards §6
43
+ # requires a successful TestPyPI publish ahead of a package's first real release; 0.9.0b0 is
44
+ # this package's first published version, so run this once before tagging v0.9.0b0. Later
45
+ # releases may skip it, or use it again as a dry run.
46
+ if: github.event_name == 'workflow_dispatch'
47
+ runs-on: ubuntu-latest
48
+ steps:
49
+ - uses: actions/checkout@v4
50
+ - uses: actions/setup-python@v5
51
+ with: { python-version: "3.12" }
52
+ - run: pip install --require-hashes -r requirements/release.lock
53
+ - run: python -m build --no-isolation
54
+ - run: twine check dist/*
55
+ # NOTE: this step cannot succeed until `weightsdb 0.2.0` and `mirrorwall 0.2.0` are
56
+ # published — both are runtime dependencies of this package and neither is on any
57
+ # index today. See requirements/README.md.
58
+ - run: pip install "dist/$(ls dist | grep .whl)[dev]"
59
+ - run: pytest -m "not live and not performance"
60
+ - name: Publish to TestPyPI
61
+ uses: pypa/gh-action-pypi-publish@release/v1
62
+ with:
63
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,100 @@
1
+ # ---- Python ----
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ MANIFEST
23
+
24
+ # ---- Packaging / build backends ----
25
+ pip-wheel-metadata/
26
+ share/python-wheels/
27
+
28
+ # ---- Testing / coverage ----
29
+ .pytest_cache/
30
+ .cache
31
+ .coverage
32
+ .coverage.*
33
+ coverage.xml
34
+ *.cover
35
+ *.py,cover
36
+ htmlcov/
37
+ nosetests.xml
38
+ .hypothesis/
39
+
40
+ # ---- Type checking / linting ----
41
+ .mypy_cache/
42
+ .dmypy.json
43
+ dmypy.json
44
+ .ruff_cache/
45
+ .pytype/
46
+
47
+ # ---- Virtual environments ----
48
+ .venv/
49
+ venv/
50
+ ENV/
51
+ env/
52
+ env.bak/
53
+ venv.bak/
54
+ .python-version
55
+
56
+ # ---- Distribution / dependency locking ----
57
+ # requirements/*.lock is deliberately NOT ignored. The lock files are committed
58
+ # inputs: CI and release jobs install from them with --require-hashes (Packaging
59
+ # and Release Standards §4, Security Standards §11), so an ignored lock file
60
+ # means a checkout that cannot build.
61
+
62
+ # ---- Editors / OS ----
63
+ .vscode/
64
+ .idea/
65
+ *.swp
66
+ *.swo
67
+ .DS_Store
68
+ Thumbs.db
69
+
70
+ # ---- Suite runtime state (this component's local data) ----
71
+ # The application writes to XDG paths at runtime (~/.config, ~/.local/share,
72
+ # ~/.local/state per Master Architecture §1.2), never inside the repository.
73
+ # These entries only guard against a developer pointing XDG_* at the repo
74
+ # during local testing.
75
+ .local/
76
+ .config/
77
+ *.sqlite3
78
+ *.sqlite3-journal
79
+ *.sqlite3-wal
80
+ *.sqlite3-shm
81
+ /data/
82
+ /logs/
83
+ /backups/
84
+ /artifacts/
85
+ /exports/
86
+
87
+ # ---- Secrets ----
88
+ # Per Security Standards §8: secrets are never committed. Config files may
89
+ # only name where a secret comes from (*_env / *_file), never the value.
90
+ .env
91
+ .env.*
92
+ *.key
93
+ *.pem
94
+ secrets.toml
95
+
96
+ # ---- Node-free JS assets (MirrorWall consumers may still use a local tool) ----
97
+ node_modules/
98
+
99
+ # ---- Build artifacts from docs generation ----
100
+ docs/api/openapi-v1.json.tmp
@@ -0,0 +1,40 @@
1
+ [importlinter]
2
+ root_package = loadcoach
3
+ include_external_packages = True
4
+
5
+ [importlinter:contract:layers]
6
+ name = LoadCoach internal layering
7
+ type = layers
8
+ layers =
9
+ web
10
+ cli
11
+ services
12
+ domain
13
+ containers = loadcoach
14
+
15
+ [importlinter:contract:web-cli-independence]
16
+ name = Web and CLI never import each other
17
+ type = independence
18
+ modules =
19
+ loadcoach.web
20
+ loadcoach.cli
21
+
22
+ [importlinter:contract:domain-purity]
23
+ name = Domain imports no frameworks
24
+ type = forbidden
25
+ source_modules = loadcoach.domain
26
+ forbidden_modules =
27
+ fastapi
28
+ starlette
29
+ sqlalchemy
30
+ typer
31
+ httpx
32
+ jinja2
33
+
34
+ [importlinter:contract:no-other-applications]
35
+ name = LoadCoach must not import other applications
36
+ type = forbidden
37
+ source_modules = loadcoach
38
+ forbidden_modules =
39
+ freeweight
40
+ ideapress
@@ -0,0 +1,22 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.6.9
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+ - repo: https://github.com/pre-commit/pre-commit-hooks
9
+ rev: v4.6.0
10
+ hooks:
11
+ - id: trailing-whitespace
12
+ - id: end-of-file-fixer
13
+ - id: check-toml
14
+ - id: check-json
15
+ - id: check-added-large-files
16
+ - id: check-merge-conflict
17
+ - id: mixed-line-ending
18
+ args: [--fix=lf]
19
+ - repo: https://github.com/gitleaks/gitleaks
20
+ rev: v8.18.4
21
+ hooks:
22
+ - id: gitleaks