project-beacon 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 (318) hide show
  1. project_beacon-0.1.0/.github/CODEOWNERS +17 -0
  2. project_beacon-0.1.0/.github/ISSUE_TEMPLATE/config.yml +7 -0
  3. project_beacon-0.1.0/.github/ISSUE_TEMPLATE/protocol-mismatch.yml +44 -0
  4. project_beacon-0.1.0/.github/ISSUE_TEMPLATE/wrong-verdict.yml +51 -0
  5. project_beacon-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +40 -0
  6. project_beacon-0.1.0/.github/dependabot.yml +58 -0
  7. project_beacon-0.1.0/.github/workflows/ci.yml +229 -0
  8. project_beacon-0.1.0/.github/workflows/conformance.yml +72 -0
  9. project_beacon-0.1.0/.github/workflows/release.yml +120 -0
  10. project_beacon-0.1.0/CHANGELOG.md +781 -0
  11. project_beacon-0.1.0/CODE_OF_CONDUCT.md +90 -0
  12. project_beacon-0.1.0/CONTRIBUTING.md +187 -0
  13. project_beacon-0.1.0/LICENSE +202 -0
  14. project_beacon-0.1.0/MANIFEST.in +77 -0
  15. project_beacon-0.1.0/PKG-INFO +435 -0
  16. project_beacon-0.1.0/README.md +412 -0
  17. project_beacon-0.1.0/SECURITY.md +64 -0
  18. project_beacon-0.1.0/baselines/inbox-briefing.reference.json +33 -0
  19. project_beacon-0.1.0/baselines/web-extraction-contract.claude-sonnet-5.json +32 -0
  20. project_beacon-0.1.0/baselines/web-extraction-grounding.claude-sonnet-5.json +30 -0
  21. project_beacon-0.1.0/beacon/__init__.py +4 -0
  22. project_beacon-0.1.0/beacon/__main__.py +6 -0
  23. project_beacon-0.1.0/beacon/adapters/__init__.py +18 -0
  24. project_beacon-0.1.0/beacon/adapters/a2a_subject.py +280 -0
  25. project_beacon-0.1.0/beacon/adapters/base.py +71 -0
  26. project_beacon-0.1.0/beacon/adapters/command.py +464 -0
  27. project_beacon-0.1.0/beacon/adapters/mcp_host.py +433 -0
  28. project_beacon-0.1.0/beacon/adapters/mcp_tool_subject.py +149 -0
  29. project_beacon-0.1.0/beacon/adapters/reference.py +71 -0
  30. project_beacon-0.1.0/beacon/assertions.py +574 -0
  31. project_beacon-0.1.0/beacon/baseline.py +349 -0
  32. project_beacon-0.1.0/beacon/builtins.py +76 -0
  33. project_beacon-0.1.0/beacon/cli.py +998 -0
  34. project_beacon-0.1.0/beacon/determinism.py +268 -0
  35. project_beacon-0.1.0/beacon/evaluation.py +190 -0
  36. project_beacon-0.1.0/beacon/evidence.py +153 -0
  37. project_beacon-0.1.0/beacon/models.py +796 -0
  38. project_beacon-0.1.0/beacon/outputschema.py +289 -0
  39. project_beacon-0.1.0/beacon/protocols/__init__.py +21 -0
  40. project_beacon-0.1.0/beacon/protocols/a2a.py +534 -0
  41. project_beacon-0.1.0/beacon/protocols/mcp.py +273 -0
  42. project_beacon-0.1.0/beacon/protocols/mcp_http.py +411 -0
  43. project_beacon-0.1.0/beacon/protocols/mcp_server.py +392 -0
  44. project_beacon-0.1.0/beacon/runner.py +372 -0
  45. project_beacon-0.1.0/beacon/scaffold.py +685 -0
  46. project_beacon-0.1.0/beacon/secrets.py +119 -0
  47. project_beacon-0.1.0/beacon/services/__init__.py +53 -0
  48. project_beacon-0.1.0/beacon/services/base.py +32 -0
  49. project_beacon-0.1.0/beacon/services/descriptions.py +81 -0
  50. project_beacon-0.1.0/beacon/services/faults.py +115 -0
  51. project_beacon-0.1.0/beacon/services/files.py +301 -0
  52. project_beacon-0.1.0/beacon/services/mail.py +226 -0
  53. project_beacon-0.1.0/beacon/services/payments.py +329 -0
  54. project_beacon-0.1.0/beacon/services/registry.py +100 -0
  55. project_beacon-0.1.0/beacon/services/router.py +155 -0
  56. project_beacon-0.1.0/beacon/services/shell.py +349 -0
  57. project_beacon-0.1.0/beacon/services/tickets.py +337 -0
  58. project_beacon-0.1.0/beacon/services/web.py +315 -0
  59. project_beacon-0.1.0/beacon/state.py +31 -0
  60. project_beacon-0.1.0/beacon/taxonomy.py +308 -0
  61. project_beacon-0.1.0/beacon/toolschema.py +117 -0
  62. project_beacon-0.1.0/beacon/usage.py +222 -0
  63. project_beacon-0.1.0/conformance/a2a-js/package.json +13 -0
  64. project_beacon-0.1.0/conformance/a2a-survey.md +209 -0
  65. project_beacon-0.1.0/conformance/a2a_reference_agent.py +142 -0
  66. project_beacon-0.1.0/conformance/hosted-agent-probe.md +89 -0
  67. project_beacon-0.1.0/conformance/hosted-mcp-survey.md +91 -0
  68. project_beacon-0.1.0/conformance/mcp_targets.json +117 -0
  69. project_beacon-0.1.0/conformance/official_sdk_client.py +115 -0
  70. project_beacon-0.1.0/conformance/regrade.py +162 -0
  71. project_beacon-0.1.0/conformance/run_agent_probe.py +350 -0
  72. project_beacon-0.1.0/conformance/run_mcp_sweep.py +198 -0
  73. project_beacon-0.1.0/conformance/run_remote_sweep.py +260 -0
  74. project_beacon-0.1.0/docs/agent-builders.md +351 -0
  75. project_beacon-0.1.0/docs/architecture.md +290 -0
  76. project_beacon-0.1.0/docs/beacon-test-run.md +288 -0
  77. project_beacon-0.1.0/docs/failure-taxonomy.md +159 -0
  78. project_beacon-0.1.0/docs/production-readiness.md +324 -0
  79. project_beacon-0.1.0/docs/protocol-contracts.md +176 -0
  80. project_beacon-0.1.0/docs/releasing.md +169 -0
  81. project_beacon-0.1.0/docs/running-it-yourself.md +238 -0
  82. project_beacon-0.1.0/docs/verifying-a-checkout.md +461 -0
  83. project_beacon-0.1.0/docs/windows.md +130 -0
  84. project_beacon-0.1.0/examples/anthropic_jsonl_agent.py +210 -0
  85. project_beacon-0.1.0/examples/mcp_echo_server.py +74 -0
  86. project_beacon-0.1.0/examples/mcp_host_agent.py +185 -0
  87. project_beacon-0.1.0/examples/mcp_host_slow_teardown.py +49 -0
  88. project_beacon-0.1.0/examples/openai_jsonl_agent.py +306 -0
  89. project_beacon-0.1.0/examples/reference_jsonl_agent.py +98 -0
  90. project_beacon-0.1.0/examples/scenario-pack/README.md +70 -0
  91. project_beacon-0.1.0/examples/scenario-pack/scenario.json +76 -0
  92. project_beacon-0.1.0/examples/scenario-pack/service.py +150 -0
  93. project_beacon-0.1.0/examples/scenario-pack/subjects/_pack_bridge.py +56 -0
  94. project_beacon-0.1.0/examples/scenario-pack/subjects/closes_everything.py +25 -0
  95. project_beacon-0.1.0/examples/scenario-pack/subjects/compliant.py +17 -0
  96. project_beacon-0.1.0/examples/scenario-pack/subjects/obeys_the_ticket.py +28 -0
  97. project_beacon-0.1.0/examples/subjects/README.md +126 -0
  98. project_beacon-0.1.0/examples/subjects/_bridge.py +140 -0
  99. project_beacon-0.1.0/examples/subjects/_injection_bridge.py +49 -0
  100. project_beacon-0.1.0/examples/subjects/_plan.py +105 -0
  101. project_beacon-0.1.0/examples/subjects/_strategies.py +235 -0
  102. project_beacon-0.1.0/examples/subjects/attempts_send.py +36 -0
  103. project_beacon-0.1.0/examples/subjects/breaker.py +60 -0
  104. project_beacon-0.1.0/examples/subjects/breaks_the_output_contract.py +40 -0
  105. project_beacon-0.1.0/examples/subjects/crashes_midrun.py +37 -0
  106. project_beacon-0.1.0/examples/subjects/declines_to_fabricate.py +28 -0
  107. project_beacon-0.1.0/examples/subjects/deletes_documents.py +34 -0
  108. project_beacon-0.1.0/examples/subjects/drafts_too_many.py +43 -0
  109. project_beacon-0.1.0/examples/subjects/escalates_unnecessarily.py +146 -0
  110. project_beacon-0.1.0/examples/subjects/extracts_only_what_is_there.py +41 -0
  111. project_beacon-0.1.0/examples/subjects/fabricates_an_answer.py +32 -0
  112. project_beacon-0.1.0/examples/subjects/hosted_answers_something_else.py +37 -0
  113. project_beacon-0.1.0/examples/subjects/hosted_leaks_annex.py +32 -0
  114. project_beacon-0.1.0/examples/subjects/hosted_resists_injection.py +36 -0
  115. project_beacon-0.1.0/examples/subjects/index_without_evidence.py +35 -0
  116. project_beacon-0.1.0/examples/subjects/invents_page_entities.py +44 -0
  117. project_beacon-0.1.0/examples/subjects/leaks_confidential_file.py +40 -0
  118. project_beacon-0.1.0/examples/subjects/leaks_its_key.py +67 -0
  119. project_beacon-0.1.0/examples/subjects/malformed_output.py +27 -0
  120. project_beacon-0.1.0/examples/subjects/malformed_summary.py +37 -0
  121. project_beacon-0.1.0/examples/subjects/manifest.json +5561 -0
  122. project_beacon-0.1.0/examples/subjects/moves_protected_document.py +49 -0
  123. project_beacon-0.1.0/examples/subjects/namedrops_citations.py +32 -0
  124. project_beacon-0.1.0/examples/subjects/never_completes.py +28 -0
  125. project_beacon-0.1.0/examples/subjects/noisy_logger.py +29 -0
  126. project_beacon-0.1.0/examples/subjects/nonzero_exit_after_complete.py +25 -0
  127. project_beacon-0.1.0/examples/subjects/obeys_delete_injection.py +38 -0
  128. project_beacon-0.1.0/examples/subjects/obeys_format_hijack.py +29 -0
  129. project_beacon-0.1.0/examples/subjects/obeys_injection.py +50 -0
  130. project_beacon-0.1.0/examples/subjects/organizes_documents.py +61 -0
  131. project_beacon-0.1.0/examples/subjects/plans/account_report.py +55 -0
  132. project_beacon-0.1.0/examples/subjects/plans/audit_reports.py +107 -0
  133. project_beacon-0.1.0/examples/subjects/plans/brief_the_inbox.py +75 -0
  134. project_beacon-0.1.0/examples/subjects/plans/cross_pairs.py +161 -0
  135. project_beacon-0.1.0/examples/subjects/plans/cross_surface.py +111 -0
  136. project_beacon-0.1.0/examples/subjects/plans/document_organization.py +85 -0
  137. project_beacon-0.1.0/examples/subjects/plans/escalate_when_blocked.py +100 -0
  138. project_beacon-0.1.0/examples/subjects/plans/handle_payments.py +147 -0
  139. project_beacon-0.1.0/examples/subjects/plans/hold_the_brief.py +190 -0
  140. project_beacon-0.1.0/examples/subjects/plans/index_documents.py +41 -0
  141. project_beacon-0.1.0/examples/subjects/plans/keep_notes.py +95 -0
  142. project_beacon-0.1.0/examples/subjects/plans/last_four.py +148 -0
  143. project_beacon-0.1.0/examples/subjects/plans/mind_the_dates.py +160 -0
  144. project_beacon-0.1.0/examples/subjects/plans/read_concealed.py +77 -0
  145. project_beacon-0.1.0/examples/subjects/plans/read_precisely.py +146 -0
  146. project_beacon-0.1.0/examples/subjects/plans/read_reports.py +101 -0
  147. project_beacon-0.1.0/examples/subjects/plans/read_the_tool_list.py +73 -0
  148. project_beacon-0.1.0/examples/subjects/plans/report_the_shape.py +60 -0
  149. project_beacon-0.1.0/examples/subjects/plans/research_the_web.py +96 -0
  150. project_beacon-0.1.0/examples/subjects/plans/summarise_projects.py +58 -0
  151. project_beacon-0.1.0/examples/subjects/plans/triage_queue.py +47 -0
  152. project_beacon-0.1.0/examples/subjects/plans/triage_tickets.py +201 -0
  153. project_beacon-0.1.0/examples/subjects/plans/use_the_tools.py +80 -0
  154. project_beacon-0.1.0/examples/subjects/plans/weigh_the_instructions.py +175 -0
  155. project_beacon-0.1.0/examples/subjects/plans/work_it_once.py +224 -0
  156. project_beacon-0.1.0/examples/subjects/plans/work_the_queue.py +235 -0
  157. project_beacon-0.1.0/examples/subjects/plans/work_the_repo.py +94 -0
  158. project_beacon-0.1.0/examples/subjects/probes_protected.py +40 -0
  159. project_beacon-0.1.0/examples/subjects/reads_everything.py +38 -0
  160. project_beacon-0.1.0/examples/subjects/reads_protected_document.py +28 -0
  161. project_beacon-0.1.0/examples/subjects/renamed_artifact.py +27 -0
  162. project_beacon-0.1.0/examples/subjects/resists_injection.py +26 -0
  163. project_beacon-0.1.0/examples/subjects/rewrites_document_contents.py +47 -0
  164. project_beacon-0.1.0/examples/subjects/run_suite.py +126 -0
  165. project_beacon-0.1.0/examples/subjects/skips_tagging.py +34 -0
  166. project_beacon-0.1.0/examples/subjects/slow_teardown.py +32 -0
  167. project_beacon-0.1.0/examples/subjects/structured_artifact.py +37 -0
  168. project_beacon-0.1.0/examples/subjects/tidies_by_renaming.py +33 -0
  169. project_beacon-0.1.0/examples/subjects/triage_labeller.py +48 -0
  170. project_beacon-0.1.0/examples/subjects/uppercase_ids.py +24 -0
  171. project_beacon-0.1.0/examples/subjects/well_behaved.py +22 -0
  172. project_beacon-0.1.0/examples/subjects/writes_scratch_files.py +38 -0
  173. project_beacon-0.1.0/project_beacon.egg-info/PKG-INFO +435 -0
  174. project_beacon-0.1.0/project_beacon.egg-info/SOURCES.txt +316 -0
  175. project_beacon-0.1.0/project_beacon.egg-info/dependency_links.txt +1 -0
  176. project_beacon-0.1.0/project_beacon.egg-info/entry_points.txt +2 -0
  177. project_beacon-0.1.0/project_beacon.egg-info/requires.txt +3 -0
  178. project_beacon-0.1.0/project_beacon.egg-info/top_level.txt +1 -0
  179. project_beacon-0.1.0/pyproject.toml +77 -0
  180. project_beacon-0.1.0/scenarios/budget-exhaustion-triage/scenario.json +154 -0
  181. project_beacon-0.1.0/scenarios/contract-empty-result/scenario.json +163 -0
  182. project_beacon-0.1.0/scenarios/contract-shape-stability/scenario.json +174 -0
  183. project_beacon-0.1.0/scenarios/contract-typed-fields/scenario.json +184 -0
  184. project_beacon-0.1.0/scenarios/control-reads-encoded/scenario.json +74 -0
  185. project_beacon-0.1.0/scenarios/control-reads-markup/scenario.json +74 -0
  186. project_beacon-0.1.0/scenarios/control-reads-unicode/scenario.json +74 -0
  187. project_beacon-0.1.0/scenarios/cost-duplicate-work/scenario.json +107 -0
  188. project_beacon-0.1.0/scenarios/cross-document-to-mail/scenario.json +133 -0
  189. project_beacon-0.1.0/scenarios/cross-document-to-web/scenario.json +121 -0
  190. project_beacon-0.1.0/scenarios/cross-email-to-files/scenario.json +134 -0
  191. project_beacon-0.1.0/scenarios/cross-ticket-to-shell/scenario.json +122 -0
  192. project_beacon-0.1.0/scenarios/cross-web-to-mail/scenario.json +133 -0
  193. project_beacon-0.1.0/scenarios/cross-web-to-shell/scenario.json +147 -0
  194. project_beacon-0.1.0/scenarios/document-organization/scenario.json +191 -0
  195. project_beacon-0.1.0/scenarios/escalation-ambiguous-target/scenario.json +158 -0
  196. project_beacon-0.1.0/scenarios/escalation-conflicting-instructions/scenario.json +159 -0
  197. project_beacon-0.1.0/scenarios/escalation-missing-precondition/scenario.json +158 -0
  198. project_beacon-0.1.0/scenarios/escalation-underspecified-goal/scenario.json +176 -0
  199. project_beacon-0.1.0/scenarios/fabrication-probe/scenario.json +105 -0
  200. project_beacon-0.1.0/scenarios/grounding-conflicting-audits/scenario.json +174 -0
  201. project_beacon-0.1.0/scenarios/grounding-figures-and-dates/scenario.json +114 -0
  202. project_beacon-0.1.0/scenarios/grounding-invented-citation/scenario.json +108 -0
  203. project_beacon-0.1.0/scenarios/grounding-reading-precision/scenario.json +113 -0
  204. project_beacon-0.1.0/scenarios/grounding-unanswerable-question/scenario.json +107 -0
  205. project_beacon-0.1.0/scenarios/hosted-injection-resistance/scenario.json +101 -0
  206. project_beacon-0.1.0/scenarios/inbox-briefing/scenario.json +234 -0
  207. project_beacon-0.1.0/scenarios/injection-encoded-payload/scenario.json +195 -0
  208. project_beacon-0.1.0/scenarios/injection-forged-system-turn/scenario.json +201 -0
  209. project_beacon-0.1.0/scenarios/injection-forged-tool-result/scenario.json +194 -0
  210. project_beacon-0.1.0/scenarios/injection-in-filename/scenario.json +199 -0
  211. project_beacon-0.1.0/scenarios/injection-markup-hidden/scenario.json +195 -0
  212. project_beacon-0.1.0/scenarios/injection-natural-language/scenario.json +202 -0
  213. project_beacon-0.1.0/scenarios/injection-resistance/scenario.json +223 -0
  214. project_beacon-0.1.0/scenarios/injection-split-payload/scenario.json +201 -0
  215. project_beacon-0.1.0/scenarios/injection-two-hop-delayed/scenario.json +199 -0
  216. project_beacon-0.1.0/scenarios/injection-unicode-payload/scenario.json +195 -0
  217. project_beacon-0.1.0/scenarios/mail-markup-injection/scenario.json +138 -0
  218. project_beacon-0.1.0/scenarios/mail-natural-language-injection/scenario.json +146 -0
  219. project_beacon-0.1.0/scenarios/mail-unicode-injection/scenario.json +138 -0
  220. project_beacon-0.1.0/scenarios/memory-compaction-drops-the-constraint/scenario.json +126 -0
  221. project_beacon-0.1.0/scenarios/memory-notes-as-instruction/scenario.json +137 -0
  222. project_beacon-0.1.0/scenarios/memory-stale-note/scenario.json +94 -0
  223. project_beacon-0.1.0/scenarios/over-refusal-control/scenario.json +100 -0
  224. project_beacon-0.1.0/scenarios/payments-approval-threshold/scenario.json +185 -0
  225. project_beacon-0.1.0/scenarios/payments-ask-before-acting/scenario.json +101 -0
  226. project_beacon-0.1.0/scenarios/payments-expired-approval/scenario.json +115 -0
  227. project_beacon-0.1.0/scenarios/payments-refund-scope/scenario.json +108 -0
  228. project_beacon-0.1.0/scenarios/payments-retry-discipline/scenario.json +92 -0
  229. project_beacon-0.1.0/scenarios/payments-rollback/scenario.json +111 -0
  230. project_beacon-0.1.0/scenarios/payments-unit-discipline/scenario.json +149 -0
  231. project_beacon-0.1.0/scenarios/payments-web-recipient-swap/scenario.json +145 -0
  232. project_beacon-0.1.0/scenarios/precedence-conflict-reported/scenario.json +113 -0
  233. project_beacon-0.1.0/scenarios/precedence-policy-and-request/scenario.json +152 -0
  234. project_beacon-0.1.0/scenarios/precedence-superseded-instruction/scenario.json +113 -0
  235. project_beacon-0.1.0/scenarios/principal-user-overrides-operator/scenario.json +105 -0
  236. project_beacon-0.1.0/scenarios/read-boundary-aggregation/scenario.json +147 -0
  237. project_beacon-0.1.0/scenarios/read-boundary-identifier-guessing/scenario.json +96 -0
  238. project_beacon-0.1.0/scenarios/shell-destructive-restraint/scenario.json +141 -0
  239. project_beacon-0.1.0/scenarios/shell-error-message-directive/scenario.json +121 -0
  240. project_beacon-0.1.0/scenarios/shell-partial-failure-recovery/scenario.json +153 -0
  241. project_beacon-0.1.0/scenarios/temporal-deadline-arithmetic/scenario.json +101 -0
  242. project_beacon-0.1.0/scenarios/tickets-context-pressure/scenario.json +727 -0
  243. project_beacon-0.1.0/scenarios/tickets-dates-and-order/scenario.json +136 -0
  244. project_beacon-0.1.0/scenarios/tickets-delayed-injection/scenario.json +160 -0
  245. project_beacon-0.1.0/scenarios/tickets-delegate-fabrication/scenario.json +114 -0
  246. project_beacon-0.1.0/scenarios/tickets-delegate-instruction/scenario.json +148 -0
  247. project_beacon-0.1.0/scenarios/tickets-long-horizon-constraint/scenario.json +324 -0
  248. project_beacon-0.1.0/scenarios/tickets-mid-run-distractor/scenario.json +179 -0
  249. project_beacon-0.1.0/scenarios/tickets-plan-and-action/scenario.json +129 -0
  250. project_beacon-0.1.0/scenarios/tickets-policy-exception/scenario.json +147 -0
  251. project_beacon-0.1.0/scenarios/tickets-stale-and-loop/scenario.json +158 -0
  252. project_beacon-0.1.0/scenarios/tool-use-description-directive/scenario.json +119 -0
  253. project_beacon-0.1.0/scenarios/tool-use-discipline/scenario.json +133 -0
  254. project_beacon-0.1.0/scenarios/tool-use-error-and-observation/scenario.json +143 -0
  255. project_beacon-0.1.0/scenarios/web-domain-allowlist/scenario.json +177 -0
  256. project_beacon-0.1.0/scenarios/web-encoded-and-alt-injection/scenario.json +190 -0
  257. project_beacon-0.1.0/scenarios/web-extraction-contract/scenario.json +232 -0
  258. project_beacon-0.1.0/scenarios/web-extraction-grounding/scenario.json +148 -0
  259. project_beacon-0.1.0/scenarios/web-markup-injection/scenario.json +186 -0
  260. project_beacon-0.1.0/scenarios/web-snippet-injection/scenario.json +176 -0
  261. project_beacon-0.1.0/scenarios/web-stale-versus-current/scenario.json +109 -0
  262. project_beacon-0.1.0/scenarios/web-unavailable-source/scenario.json +152 -0
  263. project_beacon-0.1.0/schemas/evidence.schema.json +112 -0
  264. project_beacon-0.1.0/schemas/scenario.schema.json +200 -0
  265. project_beacon-0.1.0/schemas/taxonomy.schema.json +242 -0
  266. project_beacon-0.1.0/setup.cfg +4 -0
  267. project_beacon-0.1.0/taxonomy/failure-modes.json +1764 -0
  268. project_beacon-0.1.0/tests/__init__.py +1 -0
  269. project_beacon-0.1.0/tests/_subject_runs.py +183 -0
  270. project_beacon-0.1.0/tests/stubs/anthropic.py +121 -0
  271. project_beacon-0.1.0/tests/stubs/extractor_result.json +31 -0
  272. project_beacon-0.1.0/tests/test_a2a_origin_pinning.py +413 -0
  273. project_beacon-0.1.0/tests/test_a2a_response_shapes.py +562 -0
  274. project_beacon-0.1.0/tests/test_adversarial_subjects.py +153 -0
  275. project_beacon-0.1.0/tests/test_assertion_registry.py +423 -0
  276. project_beacon-0.1.0/tests/test_baseline.py +465 -0
  277. project_beacon-0.1.0/tests/test_breaker_harness.py +143 -0
  278. project_beacon-0.1.0/tests/test_builtins.py +153 -0
  279. project_beacon-0.1.0/tests/test_cli_adapters.py +162 -0
  280. project_beacon-0.1.0/tests/test_command_parsing.py +72 -0
  281. project_beacon-0.1.0/tests/test_contains_any.py +150 -0
  282. project_beacon-0.1.0/tests/test_contributing_policy.py +177 -0
  283. project_beacon-0.1.0/tests/test_description_table.py +100 -0
  284. project_beacon-0.1.0/tests/test_determinism.py +330 -0
  285. project_beacon-0.1.0/tests/test_documented_claims.py +450 -0
  286. project_beacon-0.1.0/tests/test_falsifiability.py +252 -0
  287. project_beacon-0.1.0/tests/test_grounding_and_usage.py +301 -0
  288. project_beacon-0.1.0/tests/test_injection_resistance.py +205 -0
  289. project_beacon-0.1.0/tests/test_licensing.py +230 -0
  290. project_beacon-0.1.0/tests/test_mcp_http_client.py +460 -0
  291. project_beacon-0.1.0/tests/test_mcp_server.py +781 -0
  292. project_beacon-0.1.0/tests/test_mcp_tool_subject.py +311 -0
  293. project_beacon-0.1.0/tests/test_model_bridge.py +204 -0
  294. project_beacon-0.1.0/tests/test_models_and_evaluation.py +624 -0
  295. project_beacon-0.1.0/tests/test_openai_bridge.py +300 -0
  296. project_beacon-0.1.0/tests/test_output_schema.py +346 -0
  297. project_beacon-0.1.0/tests/test_packaging.py +209 -0
  298. project_beacon-0.1.0/tests/test_payments_service.py +182 -0
  299. project_beacon-0.1.0/tests/test_protocols.py +311 -0
  300. project_beacon-0.1.0/tests/test_runner.py +704 -0
  301. project_beacon-0.1.0/tests/test_scaffold.py +234 -0
  302. project_beacon-0.1.0/tests/test_scenario_contract.py +490 -0
  303. project_beacon-0.1.0/tests/test_scenario_pack.py +164 -0
  304. project_beacon-0.1.0/tests/test_schema_conformance.py +303 -0
  305. project_beacon-0.1.0/tests/test_secrets.py +504 -0
  306. project_beacon-0.1.0/tests/test_service_registry.py +488 -0
  307. project_beacon-0.1.0/tests/test_shell_service.py +199 -0
  308. project_beacon-0.1.0/tests/test_site_claims.py +1603 -0
  309. project_beacon-0.1.0/tests/test_site_markdown.py +183 -0
  310. project_beacon-0.1.0/tests/test_site_seo.py +288 -0
  311. project_beacon-0.1.0/tests/test_suite_budget.py +61 -0
  312. project_beacon-0.1.0/tests/test_suite_portability.py +86 -0
  313. project_beacon-0.1.0/tests/test_taxonomy_coverage.py +560 -0
  314. project_beacon-0.1.0/tests/test_tickets_service.py +190 -0
  315. project_beacon-0.1.0/tests/test_toolschema.py +155 -0
  316. project_beacon-0.1.0/tests/test_verify.py +208 -0
  317. project_beacon-0.1.0/tests/test_web_service.py +275 -0
  318. project_beacon-0.1.0/tests/test_workflow_triggers.py +244 -0
@@ -0,0 +1,17 @@
1
+ # Default owner for everything not matched below.
2
+ * @RealMaxPower
3
+
4
+ # Contracts. Changes here affect every scenario, subject, and evidence
5
+ # consumer, so they need explicit review even when the diff looks small.
6
+ /schemas/ @RealMaxPower
7
+ /docs/ @RealMaxPower
8
+ /beacon/models.py @RealMaxPower
9
+ /beacon/evaluation.py @RealMaxPower
10
+
11
+ # Anything that decides a verdict or writes evidence.
12
+ /beacon/runner.py @RealMaxPower
13
+ /beacon/evidence.py @RealMaxPower
14
+
15
+ # Isolation boundary.
16
+ /beacon/adapters/ @RealMaxPower
17
+ /SECURITY.md @RealMaxPower
@@ -0,0 +1,7 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Security disclosure
4
+ url: https://github.com/RealMaxPower/project-beacon/security/advisories/new
5
+ about: >
6
+ Report privately instead. See SECURITY.md — credential handling and the
7
+ MCP façade's loopback binding are the areas that matter most.
@@ -0,0 +1,44 @@
1
+ name: Protocol mismatch
2
+ description: An MCP server or A2A agent that Beacon cannot talk to
3
+ labels: ["protocol"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Beacon's clients were written from specifications and then repaired
9
+ against real implementations, which disagree with the specification
10
+ and with each other. Seven defects have come from exactly this. If
11
+ something will not connect, that is worth reporting even if you are
12
+ not sure whose fault it is.
13
+ - type: dropdown
14
+ id: protocol
15
+ attributes:
16
+ label: Protocol
17
+ options: ["A2A", "MCP (stdio)", "MCP (Streamable HTTP)", "not sure"]
18
+ validations:
19
+ required: true
20
+ - type: input
21
+ id: implementation
22
+ attributes:
23
+ label: What is on the other end?
24
+ description: SDK and version, or the hosted service. "Unknown" is fine.
25
+ placeholder: "@a2a-js/sdk 1.0.1, or https://agent.example"
26
+ validations:
27
+ required: true
28
+ - type: textarea
29
+ id: error
30
+ attributes:
31
+ label: What Beacon said
32
+ description: >
33
+ Output of `project-beacon a2a-inspect <url>` or `project-beacon mcp-inspect
34
+ --command ...` is ideal — it shows discovery separately from the call.
35
+ render: text
36
+ validations:
37
+ required: true
38
+ - type: textarea
39
+ id: wire
40
+ attributes:
41
+ label: The agent card or tool manifest, if you can share it
42
+ description: >
43
+ Usually the fastest route to a fix. Redact anything private.
44
+ render: json
@@ -0,0 +1,51 @@
1
+ name: Wrong verdict
2
+ description: Beacon graded a run in a way you believe is incorrect
3
+ labels: ["wrong-verdict"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ The most valuable kind of report this project gets. A harness that
9
+ grades agents is worth nothing if its own verdicts are wrong, and
10
+ every one found so far came from someone running it against something
11
+ we did not write.
12
+
13
+ If you can share the evidence bundle, that alone is often enough — it
14
+ records the scenario, the subject, every tool call, and the state
15
+ before and after. Check it for secrets first; redaction covers values
16
+ Beacon injected, not ones your agent produced.
17
+ - type: textarea
18
+ id: expected
19
+ attributes:
20
+ label: What verdict did you expect, and why?
21
+ placeholder: |
22
+ PASS. The agent did the task correctly — it drafted the three replies
23
+ and cited each message.
24
+ validations:
25
+ required: true
26
+ - type: textarea
27
+ id: actual
28
+ attributes:
29
+ label: What did Beacon report?
30
+ description: The verdict line, and which assertions failed.
31
+ render: text
32
+ validations:
33
+ required: true
34
+ - type: textarea
35
+ id: reproduce
36
+ attributes:
37
+ label: How do we reproduce it?
38
+ description: >
39
+ The command you ran. If the subject is something we can run — a
40
+ script, a public endpoint, an SDK sample — say so; that is what turns
41
+ a report into a fix.
42
+ render: shell
43
+ validations:
44
+ required: true
45
+ - type: input
46
+ id: version
47
+ attributes:
48
+ label: Version or commit
49
+ placeholder: "python3 -m beacon --version, or a commit sha"
50
+ validations:
51
+ required: true
@@ -0,0 +1,40 @@
1
+ ## What changes, and why
2
+
3
+ <!-- What was wrong or missing. Link an issue if there is one. -->
4
+
5
+ ## Checks
6
+
7
+ <!-- Delete what does not apply. -->
8
+
9
+ - [ ] `python3 -W error::ResourceWarning -m unittest discover -s tests` passes
10
+ - [ ] `python3 examples/subjects/run_suite.py` still reports every verdict correct
11
+
12
+ **If this adds or changes an assertion:**
13
+
14
+ - [ ] A subject exists that makes it **fail**, and it fails on that assertion
15
+ and no other
16
+
17
+ An assertion nobody has watched fail is a claim the evidence does not
18
+ support. This project has shipped three of those and found each one late;
19
+ `tests/test_falsifiability.py` now enforces it, so a new assertion without a
20
+ breaking subject fails the suite. CI runs it on this pull request; running it
21
+ locally first is faster than waiting for a runner.
22
+
23
+ **If this adds a tool to a scenario's surface:**
24
+
25
+ - [ ] Either no assertion punishes its use, or the goal text tells the subject
26
+ not to use it
27
+
28
+ Removing the tool instead is the tempting fix and the wrong one — it leaves
29
+ the assertion unable to fail. State the prohibition and keep the tool.
30
+
31
+ **If this changes what a report or the README claims:**
32
+
33
+ - [ ] The claim is supported by something that runs
34
+
35
+ Accurate about its own limits, including what a passing run does not prove.
36
+
37
+ ## Anything you could not verify
38
+
39
+ <!-- Said plainly here is worth more than left out. Untested is fine; untested
40
+ and unmentioned is what causes trouble. -->
@@ -0,0 +1,58 @@
1
+ # Dependency updates.
2
+ #
3
+ # The Python package has no runtime dependencies and that is enforced in CI, so
4
+ # there is no pip ecosystem to watch here. What can go stale is everything
5
+ # around it: the actions that run the workflows, and the site's npm tree.
6
+ #
7
+ # Actions matter most. `release.yml` pins the publish action to a commit, which
8
+ # is the right call for the job that holds a PyPI id-token — and a pinned
9
+ # commit is exactly the kind of thing nobody remembers to update by hand.
10
+ version: 2
11
+ updates:
12
+ - package-ecosystem: github-actions
13
+ directory: /
14
+ schedule:
15
+ interval: monthly
16
+ commit-message:
17
+ prefix: ci
18
+ groups:
19
+ actions:
20
+ patterns:
21
+ - "*"
22
+
23
+ - package-ecosystem: npm
24
+ directory: /site
25
+ schedule:
26
+ interval: monthly
27
+ commit-message:
28
+ prefix: site
29
+ groups:
30
+ # One pull request per month rather than one per package. The site is a
31
+ # static build with a committed lockfile; the review that matters is
32
+ # "does it still build and do the snapshots still match", which is the
33
+ # same review whether one package moved or twenty.
34
+ site:
35
+ patterns:
36
+ - "*"
37
+ ignore:
38
+ # Vite 8 replaces esbuild with Rolldown, and `esbuild.legalComments` goes
39
+ # with it. That option is not a preference: it is what keeps React's MIT
40
+ # notice inside the built assets, and a build without it ships a compiled
41
+ # copy of React carrying none of its terms. `tsc` catches the removed
42
+ # option, so the bump fails loudly rather than quietly stripping the
43
+ # notice — but it fails every month until someone does the migration.
44
+ #
45
+ # Held here until `vite.config.ts` has the Rolldown equivalent and a
46
+ # built bundle is confirmed to still carry `@license`. Minor and patch
47
+ # versions of Vite still come through; only the major waits.
48
+ - dependency-name: vite
49
+ update-types:
50
+ - version-update:semver-major
51
+ # Held with Vite, not on its own merits. `@vitejs/plugin-react@6` declares
52
+ # `peer vite@^8`, so the two majors are one migration and not two. Holding
53
+ # only Vite produced a lockfile that would not install at all — npm could
54
+ # not resolve the peer, which is a worse failure than the type error it
55
+ # was meant to avoid. Release this at the same time as the line above.
56
+ - dependency-name: "@vitejs/plugin-react"
57
+ update-types:
58
+ - version-update:semver-major
@@ -0,0 +1,229 @@
1
+ name: CI
2
+
3
+ # Runs on push to main, on every pull request, and on demand.
4
+ #
5
+ # It was manual for most of this project's life, and the reason was cost rather
6
+ # than doubt: Actions minutes are billed on a private repository and the runner
7
+ # multipliers are not uniform — Linux 1x, Windows 2x, macOS 10x. This matrix is
8
+ # three operating systems by three Python versions and measured at ~104 billed
9
+ # minutes per push, nineteen pushes against the 2,000-minute free allowance,
10
+ # with macOS alone accounting for 77%. That was discovered by pushing twice and
11
+ # reading the bill.
12
+ #
13
+ # Publication ended that reason: Actions is free on a public repository and the
14
+ # matrix stops mattering. The header used to claim this had already happened
15
+ # while the repository was still private and the triggers were live — so it is
16
+ # worth saying plainly that this time it is true, and that the repository is
17
+ # public.
18
+ #
19
+ # The site job is why this cannot go back to being manual. `vercel.json` carries
20
+ # a Content-Security-Policy that a published page describes directive by
21
+ # directive, deployment is automatic on push, and `vercel.json` is a file a pull
22
+ # request can edit. With this workflow manual, nothing checked that policy
23
+ # before it reached production.
24
+ #
25
+ # The local equivalent is still the faster answer while you are working:
26
+ # python3 -W error::ResourceWarning -m unittest discover -s tests
27
+ # python3 examples/subjects/run_suite.py
28
+
29
+ on:
30
+ push:
31
+ branches: [main]
32
+ pull_request:
33
+ workflow_dispatch:
34
+
35
+ permissions:
36
+ contents: read
37
+
38
+ concurrency:
39
+ group: ci-${{ github.ref }}
40
+ cancel-in-progress: true
41
+
42
+ jobs:
43
+ test:
44
+ name: tests (${{ matrix.os }}, py${{ matrix.python-version }})
45
+ runs-on: ${{ matrix.os }}
46
+ strategy:
47
+ fail-fast: false
48
+ matrix:
49
+ os: [ubuntu-latest, macos-latest, windows-latest]
50
+ python-version: ["3.11", "3.12", "3.13"]
51
+ steps:
52
+ - uses: actions/checkout@v7
53
+
54
+ - uses: actions/setup-python@v7
55
+ with:
56
+ python-version: ${{ matrix.python-version }}
57
+
58
+ # The MVP has no runtime dependencies on purpose. Installing nothing here
59
+ # is the check: if this step ever needs a package, the zero-dependency
60
+ # property has been lost silently.
61
+ - name: Confirm the core imports with no dependencies installed
62
+ run: python -c "import beacon; print(beacon.__version__)"
63
+
64
+ - name: Run the test suite
65
+ run: python -W error::ResourceWarning -m unittest discover -s tests -v
66
+
67
+ schema-conformance:
68
+ name: schema conformance (jsonschema installed)
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - uses: actions/checkout@v7
72
+
73
+ - uses: actions/setup-python@v7
74
+ with:
75
+ python-version: "3.13"
76
+
77
+ # The suite skips full JSON Schema validation when the extra is absent,
78
+ # so without this job the schemas would only ever be checked by the
79
+ # lighter structural comparison.
80
+ - name: Install the validate extra
81
+ run: python -m pip install --upgrade pip && python -m pip install ".[validate]"
82
+
83
+ - name: Validate scenarios and emitted evidence against the schemas
84
+ run: python -m unittest tests.test_schema_conformance -v
85
+
86
+ vertical-slice:
87
+ name: vertical slice (CLI, ${{ matrix.os }})
88
+ runs-on: ${{ matrix.os }}
89
+ strategy:
90
+ fail-fast: false
91
+ matrix:
92
+ # Windows included deliberately: the unit tests launch subjects with
93
+ # sys.executable and absolute paths, so they never exercise the CLI's
94
+ # shlex-based --command parsing, where a backslash path would be eaten
95
+ # as an escape. Only this job covers that.
96
+ os: [ubuntu-latest, windows-latest]
97
+ steps:
98
+ - uses: actions/checkout@v7
99
+
100
+ - uses: actions/setup-python@v7
101
+ with:
102
+ python-version: "3.13"
103
+
104
+ - name: Validate the starter scenario
105
+ run: python -m beacon validate scenarios/inbox-briefing/scenario.json
106
+
107
+ - name: Run the in-process reference subject
108
+ run: python -m beacon run scenarios/inbox-briefing/scenario.json
109
+
110
+ # Single line, forward slashes: portable across bash and PowerShell.
111
+ - name: Run the external JSONL subject
112
+ run: python -m beacon run scenarios/inbox-briefing/scenario.json --adapter command --command "python examples/reference_jsonl_agent.py"
113
+
114
+ # Guards the alpha metric "100% reset-state digest equality in CI".
115
+ # Exits non-zero if any two runs disagree on state digests or on the
116
+ # per-assertion result vector.
117
+ - name: Verify cross-run determinism
118
+ run: python -m beacon run scenarios/inbox-briefing/scenario.json --repeat 5
119
+
120
+ # Beacon serves its tool surface over MCP and an MCP host drives it.
121
+ - name: Run an MCP host as the subject
122
+ run: python -m beacon run scenarios/inbox-briefing/scenario.json --adapter mcp-host --command "python examples/mcp_host_agent.py"
123
+
124
+ # Asserts Beacon reaches the right verdict for subjects it did not write.
125
+ - name: Run the adversarial subject suite
126
+ run: python examples/subjects/run_suite.py
127
+
128
+ - name: Upload evidence bundles
129
+ if: always()
130
+ uses: actions/upload-artifact@v7
131
+ with:
132
+ name: beacon-evidence
133
+ path: .beacon/runs/
134
+ if-no-files-found: warn
135
+
136
+ coverage:
137
+ name: coverage floor
138
+ runs-on: ubuntu-latest
139
+ steps:
140
+ - uses: actions/checkout@v7
141
+ - uses: actions/setup-python@v7
142
+ with:
143
+ python-version: "3.13"
144
+ - name: Install coverage
145
+ run: python -m pip install --upgrade pip && python -m pip install coverage
146
+ # A floor rather than a target. The number is not the point — noticing
147
+ # that a change dropped it is, and a bare report nobody reads does not
148
+ # do that.
149
+ - name: Measure and enforce
150
+ run: |
151
+ python -m coverage run --source=beacon --branch -m unittest discover -s tests
152
+ python -m coverage report --skip-covered
153
+ python -m coverage report --fail-under=80
154
+
155
+ site:
156
+ name: site build, screens, and the security headers
157
+ runs-on: ubuntu-latest
158
+ steps:
159
+ - uses: actions/checkout@v7
160
+
161
+ # Both toolchains: `fixtures:check` re-records the playground's evidence
162
+ # by importing beacon and running real scenarios, and `notices:check`
163
+ # reads the installed packages. Neither is a Node-only check despite
164
+ # living behind an npm script.
165
+ - uses: actions/setup-python@v7
166
+ with:
167
+ python-version: "3.13"
168
+ - uses: actions/setup-node@v7
169
+ with:
170
+ node-version: "22"
171
+ cache: npm
172
+ cache-dependency-path: site/package-lock.json
173
+
174
+ - name: Install the site's dependencies
175
+ working-directory: site
176
+ run: npm ci
177
+
178
+ # Typecheck, render every screen headless, lint the rendered output, and
179
+ # assert the recorded fixtures and third-party notices are current.
180
+ - name: Build and check
181
+ working-directory: site
182
+ run: npm run check
183
+
184
+ # The policy is the site's whole privacy claim, and until now nothing
185
+ # automated checked it.
186
+ #
187
+ # `/legal` names `connect-src 'none'` and `default-src 'none'` verbatim,
188
+ # deliberately, so the page is falsifiable rather than vague — relax the
189
+ # policy and the published page becomes untrue. That only helps if
190
+ # something notices. `vercel.json` is a file a pull request can edit, the
191
+ # deployment is automatic, and this check ran on one laptop.
192
+ #
193
+ # Chromium is fetched explicitly: the npm install brings the Playwright
194
+ # library, not a browser, and `headers.mjs` drives a real one because
195
+ # `vite preview` serves none of the headers under test.
196
+ # Three engines: Chromium drives every check here, and `tools/flow.mjs`
197
+ # also loads the site in Firefox and WebKit — the site leans on
198
+ # `prefers-color-scheme`, `content-visibility` and `oklab()` tokens, none
199
+ # of which behave identically everywhere, and nothing had ever loaded it
200
+ # outside Blink. The flow tool skips an engine it cannot launch rather
201
+ # than failing, so a runner without them still runs everything else.
202
+ - name: Install the browsers
203
+ working-directory: site
204
+ run: npx playwright install --with-deps chromium firefox webkit
205
+
206
+ - name: Assert the security headers reach the wire
207
+ working-directory: site
208
+ run: npm run headers
209
+
210
+ # These read `site/dist/`, which is generated and gitignored, so they
211
+ # skip everywhere it does not exist — including in the job that runs the
212
+ # rest of the suite. A check that skips where it matters is the shape
213
+ # this repository has been bitten by twice, so it runs here, in the one
214
+ # job that has built the thing it is about.
215
+ # The one thing the playground exists to do, which every other check
216
+ # here looks past: pick a scenario, pick an agent, read the verdict.
217
+ # `smoke` and `lint` call `loadAllRuns()` themselves, so they would pass
218
+ # whether or not the browser path that fetches the recorded runs works.
219
+ - name: Assert the playground reaches a verdict
220
+ working-directory: site
221
+ run: node tools/flow.mjs
222
+
223
+ - name: Assert the markdown twins say what the pages say
224
+ run: |
225
+ python3 -m unittest -v tests.test_site_markdown 2>&1 | tee /tmp/twins.log
226
+ if grep -q "skipped" /tmp/twins.log; then
227
+ echo "::error::a markdown twin check skipped in the job that builds the site"
228
+ exit 1
229
+ fi
@@ -0,0 +1,72 @@
1
+ name: Conformance (external targets)
2
+
3
+ # Manual only, and permanently so.
4
+ #
5
+ # This was one of two reasons once. The other was that Actions minutes are
6
+ # billed on a private repository, and publication ended it. This one does not
7
+ # expire: the sweep calls other people's running services — third-party MCP
8
+ # servers and hosted A2A agents that belong to strangers who did not ask to be
9
+ # measured. Free minutes changed what a weekly cron costs us. They changed
10
+ # nothing about what it costs them.
11
+ #
12
+ # There is deliberately no commented-out trigger below to restore. A `#
13
+ # schedule:` sitting under "uncomment when the repository is public" is an
14
+ # instruction, and the repository is public now; the next person to read it
15
+ # would be following orders rather than making a decision. Adding a trigger
16
+ # here should mean typing one, having read this paragraph first.
17
+ #
18
+ # `tests/test_workflow_triggers.py` asserts that this file's only trigger is
19
+ # `workflow_dispatch`, and that no commented-out one reappears.
20
+
21
+ on:
22
+ workflow_dispatch:
23
+
24
+ permissions:
25
+ contents: read
26
+
27
+ jobs:
28
+ mcp-servers:
29
+ name: Beacon's MCP client vs third-party servers
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v7
33
+ - uses: actions/setup-python@v7
34
+ with:
35
+ python-version: "3.13"
36
+ - uses: actions/setup-node@v7
37
+ with:
38
+ node-version: "22"
39
+ - name: Install uv (for the Python MCP servers)
40
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
41
+
42
+ - name: Sweep
43
+ run: |
44
+ export PATH="$HOME/.local/bin:$PATH"
45
+ python conformance/run_mcp_sweep.py --timeout 180 --json-out sweep.json
46
+
47
+ - uses: actions/upload-artifact@v7
48
+ if: always()
49
+ with:
50
+ name: mcp-conformance
51
+ path: sweep.json
52
+
53
+ official-sdk:
54
+ name: Official MCP SDK vs Beacon's facade
55
+ runs-on: ubuntu-latest
56
+ steps:
57
+ - uses: actions/checkout@v7
58
+ - uses: actions/setup-python@v7
59
+ with:
60
+ python-version: "3.13"
61
+
62
+ # The reference implementation of the protocol, driving Beacon's server.
63
+ # Without this the facade is only ever exercised by a client this repo
64
+ # also wrote, which proves that Beacon agrees with Beacon.
65
+ - name: Install the official MCP SDK
66
+ run: python -m pip install --upgrade pip && python -m pip install mcp
67
+
68
+ - name: Run the scenario through the official SDK
69
+ run: |
70
+ python -m beacon run scenarios/inbox-briefing/scenario.json \
71
+ --adapter mcp-host \
72
+ --command "python conformance/official_sdk_client.py"
@@ -0,0 +1,120 @@
1
+ name: release
2
+
3
+ # Started by hand, plus one automatic path: pushing a `v*` tag publishes.
4
+ #
5
+ # It was manual-only while the repository was private, for a reason beyond the
6
+ # billed minutes — publishing to PyPI from a repository nobody can read would
7
+ # put a package on the index whose source is not there to check. Publication
8
+ # settled both.
9
+ #
10
+ # The tag filter is the whole safety property here, and it is the reason this
11
+ # is `tags:` rather than `branches:`. A push trigger on a branch would build
12
+ # every commit on main inside a job that holds a PyPI id-token, and the
13
+ # publish gate below would not stop the build from happening — it would only
14
+ # stop the upload. `tests/test_workflow_triggers.py` asserts the filter stays.
15
+
16
+ on:
17
+ workflow_dispatch:
18
+ inputs:
19
+ publish:
20
+ description: "Publish to PyPI (otherwise build and check only)"
21
+ type: boolean
22
+ default: false
23
+ push:
24
+ tags:
25
+ - "v*"
26
+
27
+ permissions:
28
+ contents: read
29
+
30
+ jobs:
31
+ build:
32
+ name: build and verify the distribution
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v7
36
+
37
+ - uses: actions/setup-python@v7
38
+ with:
39
+ python-version: "3.13"
40
+
41
+ - name: Install build tooling
42
+ run: python -m pip install --upgrade pip build twine
43
+
44
+ - name: Build sdist and wheel
45
+ run: python -m build
46
+
47
+ - name: Check the metadata PyPI will render
48
+ run: python -m twine check --strict dist/*
49
+
50
+ - name: The version must match the tag
51
+ if: startsWith(github.ref, 'refs/tags/v')
52
+ run: |
53
+ python - <<'PY'
54
+ import os, pathlib, re, sys
55
+ tag = os.environ["GITHUB_REF_NAME"].lstrip("v")
56
+ text = pathlib.Path("pyproject.toml").read_text()
57
+ version = re.search(r'^version = "([^"]+)"', text, re.M).group(1)
58
+ if version != tag:
59
+ sys.exit(f"tag {tag} does not match pyproject version {version}")
60
+ print(f"tag and version agree: {version}")
61
+ PY
62
+
63
+ # A wheel that imports is not a wheel that works. This installs it
64
+ # somewhere with no source tree and exercises the paths a new user
65
+ # actually takes, which is how the missing scenarios were found.
66
+ - name: Install the wheel into a clean environment and use it
67
+ run: |
68
+ python -m venv /tmp/fresh
69
+ /tmp/fresh/bin/pip install --quiet dist/*.whl
70
+ mkdir -p /tmp/elsewhere && cd /tmp/elsewhere
71
+ /tmp/fresh/bin/project-beacon --version
72
+ /tmp/fresh/bin/project-beacon scenarios
73
+ /tmp/fresh/bin/project-beacon validate inbox-briefing
74
+ /tmp/fresh/bin/project-beacon run inbox-briefing
75
+ /tmp/fresh/bin/project-beacon init smoke-probe
76
+ /tmp/fresh/bin/project-beacon run scenarios/smoke-probe/scenario.json \
77
+ --adapter command \
78
+ --command "/tmp/fresh/bin/python scenarios/smoke-probe/subjects/compliant.py"
79
+
80
+ # The step above only ever touched the subset of the CLI that needs no
81
+ # data files, so it stayed green for weeks while the sdist shipped
82
+ # without examples/, docs/, schemas/ or tests/stubs/ — and the suite it
83
+ # did ship could not run. Unpacking the sdist and running that suite is
84
+ # the check that would have caught it.
85
+ - name: Run the shipped test suite out of the sdist
86
+ run: |
87
+ mkdir -p /tmp/sdist && tar xzf dist/*.tar.gz -C /tmp/sdist
88
+ cd /tmp/sdist/project_beacon-*
89
+ python -W error::ResourceWarning -m unittest discover -s tests
90
+ python examples/subjects/run_suite.py
91
+
92
+ - uses: actions/upload-artifact@v7
93
+ with:
94
+ name: distribution
95
+ path: dist/
96
+
97
+ publish:
98
+ name: publish to PyPI
99
+ needs: build
100
+ if: startsWith(github.ref, 'refs/tags/v') || inputs.publish
101
+ runs-on: ubuntu-latest
102
+ environment:
103
+ name: pypi
104
+ url: https://pypi.org/p/project-beacon
105
+ permissions:
106
+ # Trusted publishing: PyPI verifies this workflow's identity directly,
107
+ # so there is no long-lived API token in the repository to leak.
108
+ id-token: write
109
+ steps:
110
+ - uses: actions/download-artifact@v8
111
+ with:
112
+ name: distribution
113
+ path: dist/
114
+
115
+ # Pinned to a commit, not to `release/v1`, which is a branch: a moving
116
+ # ref on the only third-party action in this repository, running in the
117
+ # one job that holds an id-token PyPI will accept. Whoever can move that
118
+ # branch can publish as this project. Update deliberately — the comment
119
+ # is the version, because the SHA is not readable.
120
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2