tracemantle 1.6.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 (375) hide show
  1. tracemantle-1.6.0/.gitattributes +5 -0
  2. tracemantle-1.6.0/.github/banner.svg +26 -0
  3. tracemantle-1.6.0/.github/workflows/ci.yml +112 -0
  4. tracemantle-1.6.0/.github/workflows/compare.yml +51 -0
  5. tracemantle-1.6.0/.github/workflows/release-notes.yml +98 -0
  6. tracemantle-1.6.0/.github/workflows/release.yml +130 -0
  7. tracemantle-1.6.0/.gitignore +37 -0
  8. tracemantle-1.6.0/.pre-commit-hooks.yaml +7 -0
  9. tracemantle-1.6.0/CHANGELOG.md +376 -0
  10. tracemantle-1.6.0/CONTRIBUTING.md +42 -0
  11. tracemantle-1.6.0/LICENSE +21 -0
  12. tracemantle-1.6.0/Makefile +47 -0
  13. tracemantle-1.6.0/PKG-INFO +126 -0
  14. tracemantle-1.6.0/README.md +90 -0
  15. tracemantle-1.6.0/REMEDIATION-EVIDENCE.md +765 -0
  16. tracemantle-1.6.0/action.yml +213 -0
  17. tracemantle-1.6.0/constraints-dev.txt +46 -0
  18. tracemantle-1.6.0/docs/SkillCheck_Technical_Debt_and_Product_Plan.docx +0 -0
  19. tracemantle-1.6.0/docs/case-study-silent-skill-failure.md +148 -0
  20. tracemantle-1.6.0/docs/case-study-v1-real-world-runs.md +141 -0
  21. tracemantle-1.6.0/docs/contracts.md +13 -0
  22. tracemantle-1.6.0/docs/evidence-workflow.md +68 -0
  23. tracemantle-1.6.0/docs/generated-reference.md +134 -0
  24. tracemantle-1.6.0/docs/implementation-status.md +82 -0
  25. tracemantle-1.6.0/docs/migration.md +59 -0
  26. tracemantle-1.6.0/docs/profiles.md +20 -0
  27. tracemantle-1.6.0/docs/rename-checklist.md +31 -0
  28. tracemantle-1.6.0/docs/verification/baseline-build.txt +12 -0
  29. tracemantle-1.6.0/docs/verification/baseline-controlled-performance.json +123 -0
  30. tracemantle-1.6.0/docs/verification/baseline-performance.json +92 -0
  31. tracemantle-1.6.0/docs/verification/baseline-tests.txt +74 -0
  32. tracemantle-1.6.0/docs/verification/calibration-v1.json +84 -0
  33. tracemantle-1.6.0/docs/verification/conformance.json +124 -0
  34. tracemantle-1.6.0/docs/verification/current-performance.json +133 -0
  35. tracemantle-1.6.0/docs/verification/name-audit.md +485 -0
  36. tracemantle-1.6.0/docs/verification/name-inventory-before.txt +132 -0
  37. tracemantle-1.6.0/docs/verification/performance.md +34 -0
  38. tracemantle-1.6.0/docs/verification/remote-ci.json +296 -0
  39. tracemantle-1.6.0/docs/verification/self-host.json +80 -0
  40. tracemantle-1.6.0/docs/verification/tests-python310.txt +87 -0
  41. tracemantle-1.6.0/docs/verification/tests-python312-tokenizer.txt +92 -0
  42. tracemantle-1.6.0/pyproject.toml +122 -0
  43. tracemantle-1.6.0/scripts/benchmark.py +135 -0
  44. tracemantle-1.6.0/scripts/calibrate_advisories.py +50 -0
  45. tracemantle-1.6.0/scripts/check_changelog_release.py +104 -0
  46. tracemantle-1.6.0/scripts/generate_reference.py +50 -0
  47. tracemantle-1.6.0/scripts/measure_token_error.py +144 -0
  48. tracemantle-1.6.0/scripts/regen_mode_conflict_fixture.py +75 -0
  49. tracemantle-1.6.0/scripts/regen_self_host_fixtures.py +52 -0
  50. tracemantle-1.6.0/scripts/score_corpus.py +118 -0
  51. tracemantle-1.6.0/scripts/summarize_batch.py +337 -0
  52. tracemantle-1.6.0/scripts/tracemantle_case_study_report.py +374 -0
  53. tracemantle-1.6.0/scripts/verify_artifacts.py +98 -0
  54. tracemantle-1.6.0/scripts/verify_reference_corpus.py +69 -0
  55. tracemantle-1.6.0/skills/tracemantle/SKILL.md +92 -0
  56. tracemantle-1.6.0/src/skillcheck/__init__.py +16 -0
  57. tracemantle-1.6.0/src/skillcheck/agents/__init__.py +2 -0
  58. tracemantle-1.6.0/src/skillcheck/agents/_ingest.py +2 -0
  59. tracemantle-1.6.0/src/skillcheck/agents/_response_text.py +2 -0
  60. tracemantle-1.6.0/src/skillcheck/agents/base.py +2 -0
  61. tracemantle-1.6.0/src/skillcheck/agents/claude.py +2 -0
  62. tracemantle-1.6.0/src/skillcheck/agents/codex.py +2 -0
  63. tracemantle-1.6.0/src/skillcheck/agents/cursor.py +2 -0
  64. tracemantle-1.6.0/src/skillcheck/agents/graph_base.py +2 -0
  65. tracemantle-1.6.0/src/skillcheck/agents/graph_claude.py +2 -0
  66. tracemantle-1.6.0/src/skillcheck/agents/graph_codex.py +2 -0
  67. tracemantle-1.6.0/src/skillcheck/agents/graph_cursor.py +2 -0
  68. tracemantle-1.6.0/src/skillcheck/agents/graph_parser.py +2 -0
  69. tracemantle-1.6.0/src/skillcheck/agents/parser.py +2 -0
  70. tracemantle-1.6.0/src/skillcheck/agents/schema.py +2 -0
  71. tracemantle-1.6.0/src/skillcheck/core/__init__.py +2 -0
  72. tracemantle-1.6.0/src/skillcheck/core/activation.py +2 -0
  73. tracemantle-1.6.0/src/skillcheck/core/activation_render.py +2 -0
  74. tracemantle-1.6.0/src/skillcheck/core/graph.py +2 -0
  75. tracemantle-1.6.0/src/skillcheck/core/graph_analyzers.py +2 -0
  76. tracemantle-1.6.0/src/skillcheck/core/graph_model.py +2 -0
  77. tracemantle-1.6.0/src/skillcheck/core/graph_render.py +2 -0
  78. tracemantle-1.6.0/src/skillcheck/core/history.py +2 -0
  79. tracemantle-1.6.0/src/skillcheck/core/history_io.py +2 -0
  80. tracemantle-1.6.0/src/skillcheck/core/semantic.py +2 -0
  81. tracemantle-1.6.0/src/skillcheck/core/symbolic.py +2 -0
  82. tracemantle-1.6.0/src/skillcheck/parser.py +2 -0
  83. tracemantle-1.6.0/src/skillcheck/result.py +2 -0
  84. tracemantle-1.6.0/src/skillcheck/schemas/critique-v1.json +73 -0
  85. tracemantle-1.6.0/src/skillcheck/schemas/graph-v1.json +79 -0
  86. tracemantle-1.6.0/src/tracemantle/__init__.py +15 -0
  87. tracemantle-1.6.0/src/tracemantle/__main__.py +6 -0
  88. tracemantle-1.6.0/src/tracemantle/agents/__init__.py +123 -0
  89. tracemantle-1.6.0/src/tracemantle/agents/_ingest.py +160 -0
  90. tracemantle-1.6.0/src/tracemantle/agents/_response_text.py +44 -0
  91. tracemantle-1.6.0/src/tracemantle/agents/base.py +171 -0
  92. tracemantle-1.6.0/src/tracemantle/agents/claude.py +25 -0
  93. tracemantle-1.6.0/src/tracemantle/agents/codex.py +63 -0
  94. tracemantle-1.6.0/src/tracemantle/agents/cursor.py +65 -0
  95. tracemantle-1.6.0/src/tracemantle/agents/graph_base.py +225 -0
  96. tracemantle-1.6.0/src/tracemantle/agents/graph_claude.py +25 -0
  97. tracemantle-1.6.0/src/tracemantle/agents/graph_codex.py +62 -0
  98. tracemantle-1.6.0/src/tracemantle/agents/graph_cursor.py +62 -0
  99. tracemantle-1.6.0/src/tracemantle/agents/graph_parser.py +312 -0
  100. tracemantle-1.6.0/src/tracemantle/agents/parser.py +242 -0
  101. tracemantle-1.6.0/src/tracemantle/agents/schema.py +105 -0
  102. tracemantle-1.6.0/src/tracemantle/bundle.py +145 -0
  103. tracemantle-1.6.0/src/tracemantle/cli.py +427 -0
  104. tracemantle-1.6.0/src/tracemantle/commands.py +747 -0
  105. tracemantle-1.6.0/src/tracemantle/comparison.py +135 -0
  106. tracemantle-1.6.0/src/tracemantle/config.py +76 -0
  107. tracemantle-1.6.0/src/tracemantle/config_loader.py +255 -0
  108. tracemantle-1.6.0/src/tracemantle/core/__init__.py +101 -0
  109. tracemantle-1.6.0/src/tracemantle/core/activation.py +197 -0
  110. tracemantle-1.6.0/src/tracemantle/core/activation_render.py +80 -0
  111. tracemantle-1.6.0/src/tracemantle/core/graph.py +486 -0
  112. tracemantle-1.6.0/src/tracemantle/core/graph_analyzers.py +349 -0
  113. tracemantle-1.6.0/src/tracemantle/core/graph_model.py +110 -0
  114. tracemantle-1.6.0/src/tracemantle/core/graph_render.py +101 -0
  115. tracemantle-1.6.0/src/tracemantle/core/history.py +354 -0
  116. tracemantle-1.6.0/src/tracemantle/core/history_io.py +276 -0
  117. tracemantle-1.6.0/src/tracemantle/core/semantic.py +189 -0
  118. tracemantle-1.6.0/src/tracemantle/core/symbolic.py +82 -0
  119. tracemantle-1.6.0/src/tracemantle/dependencies.py +91 -0
  120. tracemantle-1.6.0/src/tracemantle/display.py +14 -0
  121. tracemantle-1.6.0/src/tracemantle/evidence.py +106 -0
  122. tracemantle-1.6.0/src/tracemantle/formatters.py +330 -0
  123. tracemantle-1.6.0/src/tracemantle/history_store.py +81 -0
  124. tracemantle-1.6.0/src/tracemantle/io_limits.py +200 -0
  125. tracemantle-1.6.0/src/tracemantle/markdown.py +91 -0
  126. tracemantle-1.6.0/src/tracemantle/modes.py +222 -0
  127. tracemantle-1.6.0/src/tracemantle/parser.py +142 -0
  128. tracemantle-1.6.0/src/tracemantle/policy.py +119 -0
  129. tracemantle-1.6.0/src/tracemantle/product_commands.py +139 -0
  130. tracemantle-1.6.0/src/tracemantle/profiles/compatibility-v1.json +40 -0
  131. tracemantle-1.6.0/src/tracemantle/promptfoo.py +77 -0
  132. tracemantle-1.6.0/src/tracemantle/py.typed +0 -0
  133. tracemantle-1.6.0/src/tracemantle/result.py +68 -0
  134. tracemantle-1.6.0/src/tracemantle/rules/__init__.py +171 -0
  135. tracemantle-1.6.0/src/tracemantle/rules/compat.py +221 -0
  136. tracemantle-1.6.0/src/tracemantle/rules/description.py +427 -0
  137. tracemantle-1.6.0/src/tracemantle/rules/disclosure.py +155 -0
  138. tracemantle-1.6.0/src/tracemantle/rules/frontmatter.py +45 -0
  139. tracemantle-1.6.0/src/tracemantle/rules/frontmatter_common.py +63 -0
  140. tracemantle-1.6.0/src/tracemantle/rules/frontmatter_description.py +144 -0
  141. tracemantle-1.6.0/src/tracemantle/rules/frontmatter_fields.py +96 -0
  142. tracemantle-1.6.0/src/tracemantle/rules/frontmatter_name.py +186 -0
  143. tracemantle-1.6.0/src/tracemantle/rules/references.py +29 -0
  144. tracemantle-1.6.0/src/tracemantle/rules/sizing.py +48 -0
  145. tracemantle-1.6.0/src/tracemantle/rules/template.py +26 -0
  146. tracemantle-1.6.0/src/tracemantle/schemas/critique-v1.json +73 -0
  147. tracemantle-1.6.0/src/tracemantle/schemas/evidence-v1.json +165 -0
  148. tracemantle-1.6.0/src/tracemantle/schemas/graph-v1.json +79 -0
  149. tracemantle-1.6.0/src/tracemantle/storage.py +95 -0
  150. tracemantle-1.6.0/src/tracemantle/template_detection.py +42 -0
  151. tracemantle-1.6.0/src/tracemantle/tokenizer.py +40 -0
  152. tracemantle-1.6.0/tests/__init__.py +0 -0
  153. tracemantle-1.6.0/tests/conftest.py +43 -0
  154. tracemantle-1.6.0/tests/fixtures/bad_body_long.md +505 -0
  155. tracemantle-1.6.0/tests/fixtures/bad_desc_empty.md +6 -0
  156. tracemantle-1.6.0/tests/fixtures/bad_desc_person.md +6 -0
  157. tracemantle-1.6.0/tests/fixtures/bad_desc_quality.md +8 -0
  158. tracemantle-1.6.0/tests/fixtures/bad_field_typo.md +7 -0
  159. tracemantle-1.6.0/tests/fixtures/bad_frontmatter_int.md +4 -0
  160. tracemantle-1.6.0/tests/fixtures/bad_frontmatter_list.md +6 -0
  161. tracemantle-1.6.0/tests/fixtures/bad_frontmatter_string.md +4 -0
  162. tracemantle-1.6.0/tests/fixtures/bad_name_caps.md +6 -0
  163. tracemantle-1.6.0/tests/fixtures/bad_name_consecutive_hyphens.md +6 -0
  164. tracemantle-1.6.0/tests/fixtures/bad_name_leading_hyphen.md +6 -0
  165. tracemantle-1.6.0/tests/fixtures/bad_name_long.md +6 -0
  166. tracemantle-1.6.0/tests/fixtures/bad_name_reserved.md +6 -0
  167. tracemantle-1.6.0/tests/fixtures/bad_name_trailing_hyphen.md +6 -0
  168. tracemantle-1.6.0/tests/fixtures/bad_no_frontmatter.md +4 -0
  169. tracemantle-1.6.0/tests/fixtures/bad_refs.md +14 -0
  170. tracemantle-1.6.0/tests/fixtures/canvas_design_pattern.md +6 -0
  171. tracemantle-1.6.0/tests/fixtures/claude_api_name.md +6 -0
  172. tracemantle-1.6.0/tests/fixtures/claude_only_fields.md +13 -0
  173. tracemantle-1.6.0/tests/fixtures/critique/malformed_prose_preamble.txt +10 -0
  174. tracemantle-1.6.0/tests/fixtures/critique/minimal_valid.md +6 -0
  175. tracemantle-1.6.0/tests/fixtures/critique/multi_section.md +31 -0
  176. tracemantle-1.6.0/tests/fixtures/critique/response_ansi_injection.json +10 -0
  177. tracemantle-1.6.0/tests/fixtures/critique/response_clean.json +8 -0
  178. tracemantle-1.6.0/tests/fixtures/critique/response_contradiction.json +14 -0
  179. tracemantle-1.6.0/tests/fixtures/critique/response_findings_mixed.json +27 -0
  180. tracemantle-1.6.0/tests/fixtures/critique/response_malformed.json +2 -0
  181. tracemantle-1.6.0/tests/fixtures/critique/response_schema_error.json +8 -0
  182. tracemantle-1.6.0/tests/fixtures/critique/response_warnings.json +8 -0
  183. tracemantle-1.6.0/tests/fixtures/critique/with_code_blocks.md +34 -0
  184. tracemantle-1.6.0/tests/fixtures/cursor_desc_folded_keep.md +12 -0
  185. tracemantle-1.6.0/tests/fixtures/cursor_desc_folded_strip.md +12 -0
  186. tracemantle-1.6.0/tests/fixtures/cursor_desc_literal.md +12 -0
  187. tracemantle-1.6.0/tests/fixtures/frontmatter_heading_partial.md +10 -0
  188. tracemantle-1.6.0/tests/fixtures/frontmatter_name_as_heading.md +10 -0
  189. tracemantle-1.6.0/tests/fixtures/golden/report.agent.txt +12 -0
  190. tracemantle-1.6.0/tests/fixtures/golden/report.github.txt +5 -0
  191. tracemantle-1.6.0/tests/fixtures/golden/report.json.txt +84 -0
  192. tracemantle-1.6.0/tests/fixtures/golden/report.markdown.txt +22 -0
  193. tracemantle-1.6.0/tests/fixtures/golden/report.text.txt +13 -0
  194. tracemantle-1.6.0/tests/fixtures/golden/report.text_plain.txt +10 -0
  195. tracemantle-1.6.0/tests/fixtures/graph/skill_basic_io.md +23 -0
  196. tracemantle-1.6.0/tests/fixtures/graph/skill_duplicate_section.md +17 -0
  197. tracemantle-1.6.0/tests/fixtures/graph/skill_duplicate_tools.md +10 -0
  198. tracemantle-1.6.0/tests/fixtures/graph/skill_empty_capability.md +13 -0
  199. tracemantle-1.6.0/tests/fixtures/graph/skill_frontmatter_tools.md +11 -0
  200. tracemantle-1.6.0/tests/fixtures/graph/skill_imperative_capabilities.md +25 -0
  201. tracemantle-1.6.0/tests/fixtures/graph/skill_no_structure.md +10 -0
  202. tracemantle-1.6.0/tests/fixtures/graph/skill_orphan_capability.md +12 -0
  203. tracemantle-1.6.0/tests/fixtures/graph/skill_section_aliases.md +18 -0
  204. tracemantle-1.6.0/tests/fixtures/graph/skill_unproduced_output.md +15 -0
  205. tracemantle-1.6.0/tests/fixtures/graph/skill_unreferenced_tool.md +13 -0
  206. tracemantle-1.6.0/tests/fixtures/graph/skill_unused_input.md +20 -0
  207. tracemantle-1.6.0/tests/fixtures/graph_responses/response_bad_line.json +13 -0
  208. tracemantle-1.6.0/tests/fixtures/graph_responses/response_clean.json +54 -0
  209. tracemantle-1.6.0/tests/fixtures/graph_responses/response_extra_capabilities.json +57 -0
  210. tracemantle-1.6.0/tests/fixtures/graph_responses/response_malformed.json +3 -0
  211. tracemantle-1.6.0/tests/fixtures/graph_responses/response_schema_error.json +6 -0
  212. tracemantle-1.6.0/tests/fixtures/graph_responses/response_with_contradiction.json +55 -0
  213. tracemantle-1.6.0/tests/fixtures/history/ledger_bad_version.json +1 -0
  214. tracemantle-1.6.0/tests/fixtures/history/ledger_malformed.json +1 -0
  215. tracemantle-1.6.0/tests/fixtures/history/ledger_malformed_entry.json +1 -0
  216. tracemantle-1.6.0/tests/fixtures/history/ledger_multi_run_passing.json +69 -0
  217. tracemantle-1.6.0/tests/fixtures/history/ledger_one_run.json +27 -0
  218. tracemantle-1.6.0/tests/fixtures/history/ledger_root_list.json +1 -0
  219. tracemantle-1.6.0/tests/fixtures/history/ledger_runs_not_list.json +1 -0
  220. tracemantle-1.6.0/tests/fixtures/history/ledger_with_regression.json +48 -0
  221. tracemantle-1.6.0/tests/fixtures/homepage_field.md +7 -0
  222. tracemantle-1.6.0/tests/fixtures/license_field.md +7 -0
  223. tracemantle-1.6.0/tests/fixtures/metadata_field.md +18 -0
  224. tracemantle-1.6.0/tests/fixtures/mode_conflicts.json +362 -0
  225. tracemantle-1.6.0/tests/fixtures/non_template_bracket_acronym.md +7 -0
  226. tracemantle-1.6.0/tests/fixtures/non_template_with_placeholder_word.md +6 -0
  227. tracemantle-1.6.0/tests/fixtures/repository_field.md +7 -0
  228. tracemantle-1.6.0/tests/fixtures/self_host/critique_clean.json +8 -0
  229. tracemantle-1.6.0/tests/fixtures/self_host/graph_clean.json +130 -0
  230. tracemantle-1.6.0/tests/fixtures/template_bracket_placeholder.md +7 -0
  231. tracemantle-1.6.0/tests/fixtures/template_explicit_flag.md +7 -0
  232. tracemantle-1.6.0/tests/fixtures/template_placeholder_description.md +6 -0
  233. tracemantle-1.6.0/tests/fixtures/template_with_real_violations.md +7 -0
  234. tracemantle-1.6.0/tests/fixtures/templates/SKILL.md +6 -0
  235. tracemantle-1.6.0/tests/fixtures/tracemantle/README.md +5 -0
  236. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/bad-compatibility.md +7 -0
  237. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/bad-metadata-values.md +8 -0
  238. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/bad-metadata.md +7 -0
  239. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/boolean-key.md +7 -0
  240. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/changed-permission.json +4 -0
  241. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/control.json +4 -0
  242. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/controls.md +6 -0
  243. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/description-change.json +4 -0
  244. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/duplicate-name.md +7 -0
  245. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/duplicate-nested.md +9 -0
  246. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/external-links.md +7 -0
  247. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/failed.json +4 -0
  248. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/fenced-links.md +12 -0
  249. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/future.json +4 -0
  250. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/helper-change.json +4 -0
  251. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/incomplete.json +4 -0
  252. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/inferred.json +4 -0
  253. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/infrastructure.json +4 -0
  254. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/inline-close.md +4 -0
  255. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/mapping-key.md +8 -0
  256. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/missing-evidence.json +4 -0
  257. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/model-alias.json +4 -0
  258. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/model-judgment.json +4 -0
  259. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/nonstring-key.md +7 -0
  260. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/recursive-alias.md +8 -0
  261. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/reference-links.md +8 -0
  262. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/skipped.json +4 -0
  263. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/stale.json +4 -0
  264. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/untrusted.json +4 -0
  265. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/valid-alias.md +9 -0
  266. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/valid-bom.md +6 -0
  267. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/valid-crlf.md +6 -0
  268. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/valid-empty-block.md +3 -0
  269. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/valid-multiline.md +8 -0
  270. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/valid-standard.md +10 -0
  271. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/wrong-bundle.json +4 -0
  272. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/wrong-checker.json +4 -0
  273. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/wrong-config.json +4 -0
  274. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/wrong-fixture.json +4 -0
  275. tracemantle-1.6.0/tests/fixtures/tracemantle/cases/wrong-profile.json +4 -0
  276. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/code-json.txt +100 -0
  277. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/code-python.txt +200 -0
  278. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/large.txt +1200 -0
  279. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/prose-ar.txt +30 -0
  280. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/prose-en.txt +30 -0
  281. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/prose-es.txt +30 -0
  282. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/prose-ja.txt +30 -0
  283. tracemantle-1.6.0/tests/fixtures/tracemantle/held-out/small.txt +1 -0
  284. tracemantle-1.6.0/tests/fixtures/unknown_field.md +7 -0
  285. tracemantle-1.6.0/tests/fixtures/upstream/promptfoo-0.118.10/LICENSE +19 -0
  286. tracemantle-1.6.0/tests/fixtures/upstream/promptfoo-0.118.10/PROVENANCE.json +9 -0
  287. tracemantle-1.6.0/tests/fixtures/upstream/promptfoo-0.118.10/sample-export.json +692 -0
  288. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/LICENSE +202 -0
  289. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/PROVENANCE.json +55 -0
  290. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/README.md +113 -0
  291. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/pyproject.toml +30 -0
  292. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/skills_ref/__init__.py +20 -0
  293. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/skills_ref/cli.py +105 -0
  294. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/skills_ref/errors.py +25 -0
  295. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/skills_ref/models.py +39 -0
  296. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/skills_ref/parser.py +112 -0
  297. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/skills_ref/prompt.py +58 -0
  298. tracemantle-1.6.0/tests/fixtures/upstream/skills-ref-69ef37e/skills_ref/validator.py +177 -0
  299. tracemantle-1.6.0/tests/fixtures/user_extension/skillcheck.toml +2 -0
  300. tracemantle-1.6.0/tests/fixtures/user_extension/user_extension_field.md +7 -0
  301. tracemantle-1.6.0/tests/fixtures/valid_basic.md +6 -0
  302. tracemantle-1.6.0/tests/fixtures/valid_full.md +23 -0
  303. tracemantle-1.6.0/tests/fixtures/valid_good_desc.md +8 -0
  304. tracemantle-1.6.0/tests/golden/claude-doc-helper/SKILL.md +6 -0
  305. tracemantle-1.6.0/tests/golden/claude-doc-helper/expected.txt +2 -0
  306. tracemantle-1.6.0/tests/golden/clean-baseline/SKILL.md +6 -0
  307. tracemantle-1.6.0/tests/golden/clean-baseline/expected.txt +1 -0
  308. tracemantle-1.6.0/tests/golden/person-voice-first/SKILL.md +6 -0
  309. tracemantle-1.6.0/tests/golden/person-voice-first/expected.txt +2 -0
  310. tracemantle-1.6.0/tests/golden/person-voice-second/SKILL.md +6 -0
  311. tracemantle-1.6.0/tests/golden/person-voice-second/expected.txt +2 -0
  312. tracemantle-1.6.0/tests/golden/unknown-fields/SKILL.md +9 -0
  313. tracemantle-1.6.0/tests/golden/unknown-fields/expected.txt +3 -0
  314. tracemantle-1.6.0/tests/test_activation.py +211 -0
  315. tracemantle-1.6.0/tests/test_agent_prompts.py +199 -0
  316. tracemantle-1.6.0/tests/test_agent_prompts_smoke.py +152 -0
  317. tracemantle-1.6.0/tests/test_batch15_summarize.py +86 -0
  318. tracemantle-1.6.0/tests/test_build_plan_acceptance.py +142 -0
  319. tracemantle-1.6.0/tests/test_build_plan_core.py +164 -0
  320. tracemantle-1.6.0/tests/test_build_plan_product.py +276 -0
  321. tracemantle-1.6.0/tests/test_changelog_release_check.py +99 -0
  322. tracemantle-1.6.0/tests/test_cli.py +429 -0
  323. tracemantle-1.6.0/tests/test_cli_critique.py +320 -0
  324. tracemantle-1.6.0/tests/test_cli_critique_agent.py +142 -0
  325. tracemantle-1.6.0/tests/test_cli_graph.py +224 -0
  326. tracemantle-1.6.0/tests/test_cli_graph_agent.py +303 -0
  327. tracemantle-1.6.0/tests/test_cli_history.py +453 -0
  328. tracemantle-1.6.0/tests/test_compat.py +286 -0
  329. tracemantle-1.6.0/tests/test_compat_data_freshness.py +97 -0
  330. tracemantle-1.6.0/tests/test_config_loader.py +88 -0
  331. tracemantle-1.6.0/tests/test_config_validation.py +190 -0
  332. tracemantle-1.6.0/tests/test_critique_parser.py +303 -0
  333. tracemantle-1.6.0/tests/test_critique_prompt.py +137 -0
  334. tracemantle-1.6.0/tests/test_critique_schema.py +212 -0
  335. tracemantle-1.6.0/tests/test_description.py +289 -0
  336. tracemantle-1.6.0/tests/test_description_scorer_tuning.py +220 -0
  337. tracemantle-1.6.0/tests/test_disclosure.py +198 -0
  338. tracemantle-1.6.0/tests/test_explain_score.py +207 -0
  339. tracemantle-1.6.0/tests/test_fail_on_regression.py +197 -0
  340. tracemantle-1.6.0/tests/test_format_github.py +118 -0
  341. tracemantle-1.6.0/tests/test_formatter_golden.py +238 -0
  342. tracemantle-1.6.0/tests/test_frontmatter.py +416 -0
  343. tracemantle-1.6.0/tests/test_golden_warnings.py +125 -0
  344. tracemantle-1.6.0/tests/test_graph_analyzers.py +194 -0
  345. tracemantle-1.6.0/tests/test_graph_divergence.py +170 -0
  346. tracemantle-1.6.0/tests/test_graph_heuristic.py +556 -0
  347. tracemantle-1.6.0/tests/test_graph_parser.py +283 -0
  348. tracemantle-1.6.0/tests/test_graph_prompts.py +179 -0
  349. tracemantle-1.6.0/tests/test_graph_render.py +186 -0
  350. tracemantle-1.6.0/tests/test_history_io.py +276 -0
  351. tracemantle-1.6.0/tests/test_history_model.py +259 -0
  352. tracemantle-1.6.0/tests/test_history_regression.py +135 -0
  353. tracemantle-1.6.0/tests/test_ingest_sanitization.py +71 -0
  354. tracemantle-1.6.0/tests/test_io_limits.py +128 -0
  355. tracemantle-1.6.0/tests/test_measure_token_error.py +73 -0
  356. tracemantle-1.6.0/tests/test_mode_conflicts.py +215 -0
  357. tracemantle-1.6.0/tests/test_name_compliance.py +119 -0
  358. tracemantle-1.6.0/tests/test_parser.py +95 -0
  359. tracemantle-1.6.0/tests/test_pre_commit.py +85 -0
  360. tracemantle-1.6.0/tests/test_published_schemas.py +170 -0
  361. tracemantle-1.6.0/tests/test_readme_test_count_claim.py +54 -0
  362. tracemantle-1.6.0/tests/test_references.py +292 -0
  363. tracemantle-1.6.0/tests/test_self_host.py +148 -0
  364. tracemantle-1.6.0/tests/test_semantic_bridge.py +400 -0
  365. tracemantle-1.6.0/tests/test_sizing.py +94 -0
  366. tracemantle-1.6.0/tests/test_template_detection.py +24 -0
  367. tracemantle-1.6.0/tests/test_tokenizer.py +56 -0
  368. tracemantle-1.6.0/tests/test_type_annotations.py +20 -0
  369. tracemantle-1.6.0/tests/test_untrusted_input.py +307 -0
  370. tracemantle-1.6.0/tests/test_v1_2_false_positive_fixes.py +189 -0
  371. tracemantle-1.6.0/tests/test_v1_architecture.py +87 -0
  372. tracemantle-1.6.0/tests/test_v1_completion.py +91 -0
  373. tracemantle-1.6.0/tests/test_version_coherence.py +117 -0
  374. tracemantle-1.6.0/tests/test_yaml_anchors.py +99 -0
  375. tracemantle-1.6.0/tests/test_yaml_types.py +120 -0
@@ -0,0 +1,5 @@
1
+ # Keep source and digest-verified fixtures identical on every CI platform.
2
+ * text=auto eol=lf
3
+ # This parser control must retain its literal CRLF bytes.
4
+ tests/fixtures/tracemantle/cases/valid-crlf.md -text whitespace=cr-at-eol
5
+ *.docx binary
@@ -0,0 +1,26 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 200" fill="none">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="0" x2="800" y2="200" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0%" stop-color="#0f0f23"/>
5
+ <stop offset="100%" stop-color="#1a1a3e"/>
6
+ </linearGradient>
7
+ <linearGradient id="accent" x1="0" y1="0" x2="1" y2="1">
8
+ <stop offset="0%" stop-color="#d4a574"/>
9
+ <stop offset="100%" stop-color="#e8c9a0"/>
10
+ </linearGradient>
11
+ </defs>
12
+ <rect width="800" height="200" rx="12" fill="url(#bg)"/>
13
+ <!-- check icon -->
14
+ <g transform="translate(220, 55)">
15
+ <circle cx="40" cy="40" r="36" stroke="url(#accent)" stroke-width="3" fill="none" opacity="0.9"/>
16
+ <polyline points="22,42 36,56 60,28" stroke="#4ade80" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
17
+ </g>
18
+ <!-- title -->
19
+ <text x="400" y="108" text-anchor="middle" font-family="ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace" font-size="42" font-weight="700" fill="url(#accent)">
20
+ <tspan dx="52">TraceMantle</tspan>
21
+ </text>
22
+ <!-- subtitle -->
23
+ <text x="400" y="142" text-anchor="middle" font-family="-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif" font-size="15" fill="#8b8ba7" letter-spacing="0.5">
24
+ Static analysis and release evidence for agent skill bundles
25
+ </text>
26
+ </svg>
@@ -0,0 +1,112 @@
1
+ name: CI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ workflow_call:
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+ branches: [main]
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ test:
16
+ runs-on: ${{ matrix.os }}
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ os: [ubuntu-latest, macos-latest, windows-latest]
21
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
22
+
23
+ steps:
24
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
25
+
26
+ - name: Set up Python ${{ matrix.python-version }}
27
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+
31
+ - name: Install dependencies
32
+ run: |
33
+ python -m pip install --upgrade pip
34
+ pip install -c constraints-dev.txt -e ".[dev]"
35
+
36
+ - name: Run tests
37
+ # The coverage floor is applied here rather than in pyproject addopts,
38
+ # which would also fail single-file local runs. See the comment there.
39
+ run: python -m pytest tests/ -v --tb=short --cov-fail-under=80
40
+
41
+ lint:
42
+ runs-on: ubuntu-latest
43
+ steps:
44
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
45
+
46
+ - name: Set up Python
47
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
48
+ with:
49
+ python-version: "3.12"
50
+
51
+ - name: Install dev dependencies
52
+ run: pip install -c constraints-dev.txt -e ".[dev]"
53
+
54
+ - name: Ruff check
55
+ run: ruff check src tests scripts
56
+
57
+ - run: python scripts/generate_reference.py --check
58
+
59
+ - name: Mypy strict
60
+ # Bare mypy uses the [tool.mypy] files list (src plus the checked-in
61
+ # scripts), so the utility scripts are type-gated alongside the package.
62
+ run: mypy
63
+
64
+ package:
65
+ needs: [test, lint, tokenizer]
66
+ # Smoke-check that the project builds and the wheel installs on every push
67
+ # and PR. Attestation and PyPI publishing happen only on tag pushes, in
68
+ # release.yml, so this job stays read-only.
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
72
+
73
+ - name: Set up Python
74
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
75
+ with:
76
+ python-version: "3.12"
77
+
78
+ - name: Install reproducible build tools
79
+ run: pip install -c constraints-dev.txt build hatchling
80
+
81
+ - name: Build exact release artifacts
82
+ run: python -m build --no-isolation --outdir candidate-dist
83
+
84
+ - name: Verify wheel and source installs
85
+ run: python scripts/verify_artifacts.py candidate-dist --output artifact-verification.json
86
+
87
+ - name: Retain verified artifacts
88
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
89
+ with:
90
+ name: tracemantle-dist
91
+ path: candidate-dist/*
92
+ if-no-files-found: error
93
+
94
+ - name: Retain artifact verification report
95
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
96
+ with:
97
+ name: tracemantle-verification
98
+ path: artifact-verification.json
99
+ if-no-files-found: error
100
+
101
+ tokenizer:
102
+ runs-on: ubuntu-latest
103
+ steps:
104
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
105
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
106
+ with:
107
+ python-version: "3.12"
108
+ - run: pip install -c constraints-dev.txt -e ".[dev,tiktoken]"
109
+ - run: python -m pytest tests/ -q --cov-fail-under=80
110
+ - run: python scripts/calibrate_advisories.py --output calibration.json
111
+ - run: pip install -c constraints-dev.txt strictyaml click
112
+ - run: python scripts/verify_reference_corpus.py --output conformance.json
@@ -0,0 +1,51 @@
1
+ name: Trusted bundle comparison
2
+ on:
3
+ workflow_dispatch:
4
+ inputs:
5
+ base_revision:
6
+ description: Full trusted base commit SHA
7
+ required: true
8
+ candidate_revision:
9
+ description: Candidate commit SHA to inspect without executing
10
+ required: true
11
+ bundle_path:
12
+ description: Relative bundle directory in both revisions
13
+ required: true
14
+ default: skills/tracemantle
15
+ permissions:
16
+ contents: read
17
+ jobs:
18
+ compare:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
22
+ with:
23
+ ref: ${{ inputs.base_revision }}
24
+ path: trusted
25
+ persist-credentials: false
26
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
27
+ with:
28
+ ref: ${{ inputs.candidate_revision }}
29
+ path: candidate
30
+ persist-credentials: false
31
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
32
+ with:
33
+ python-version: "3.12"
34
+ - name: Install only the trusted tool
35
+ run: pip install -c trusted/constraints-dev.txt ./trusted
36
+ - name: Compare against immutable trusted policy
37
+ env:
38
+ BASE_REVISION: ${{ inputs.base_revision }}
39
+ BUNDLE_PATH: ${{ inputs.bundle_path }}
40
+ shell: bash
41
+ run: |
42
+ set -euo pipefail
43
+ args=()
44
+ if [ -d candidate/evidence/records ]; then
45
+ while IFS= read -r -d '' record; do
46
+ args+=(--evidence "$record")
47
+ done < <(find candidate/evidence/records -maxdepth 1 -type f -name '*.json' -print0)
48
+ fi
49
+ tracemantle compare "trusted/$BUNDLE_PATH" "candidate/$BUNDLE_PATH" \
50
+ --trusted-root trusted --base-revision "$BASE_REVISION" \
51
+ --format github "${args[@]}"
@@ -0,0 +1,98 @@
1
+ name: Release notes
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: write
9
+ pull-requests: write
10
+
11
+ jobs:
12
+ promote-unreleased:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
16
+ with:
17
+ ref: main
18
+ fetch-depth: 0
19
+
20
+ - name: Promote [Unreleased] to released version heading
21
+ id: promote
22
+ shell: bash
23
+ env:
24
+ TAG: ${{ github.event.release.tag_name }}
25
+ run: |
26
+ set -euo pipefail
27
+ version="${TAG#v}"
28
+ today=$(date -u +%Y-%m-%d)
29
+ python3 - "$version" "$today" <<'PY'
30
+ import re
31
+ import sys
32
+ from pathlib import Path
33
+
34
+ version, today = sys.argv[1], sys.argv[2]
35
+ path = Path("CHANGELOG.md")
36
+ text = path.read_text(encoding="utf-8")
37
+
38
+ # Locate the [Unreleased] block: its content runs until the next ## heading.
39
+ match = re.search(r"^##\s*\[Unreleased\]\s*\n", text, re.MULTILINE)
40
+ if not match:
41
+ print("CHANGELOG.md has no [Unreleased] heading; nothing to promote.")
42
+ sys.exit(0)
43
+
44
+ start = match.end()
45
+ next_heading = re.search(r"^##\s*\[", text[start:], re.MULTILINE)
46
+ end = start + next_heading.start() if next_heading else len(text)
47
+ body = text[start:end].strip()
48
+
49
+ if not body:
50
+ print("[Unreleased] is empty; nothing to promote.")
51
+ # Signal an empty promotion to the next step.
52
+ Path("PROMOTED.flag").write_text("empty", encoding="utf-8")
53
+ sys.exit(0)
54
+
55
+ # A heading for the released version already exists, so the developer
56
+ # promoted by hand. Promoting again would duplicate entries, but going
57
+ # quiet is what stranded nine shipped fixes outside the v1.4.1 notes:
58
+ # the hand promotion was partial and the remainder stayed here. There
59
+ # is content and nowhere safe to put it, so fail instead of no-oping.
60
+ if re.search(rf"^##\s*\[{re.escape(version)}\]", text, re.MULTILINE):
61
+ print(
62
+ f"[{version}] heading already present, but [Unreleased] still has entries. "
63
+ f"They shipped in {version} and are not recorded under it. Move them under "
64
+ f"[{version}] by hand, or hold them for the next version.",
65
+ file=sys.stderr,
66
+ )
67
+ print(body, file=sys.stderr)
68
+ sys.exit(1)
69
+
70
+ new_block = (
71
+ f"## [Unreleased]\n\n"
72
+ f"## [{version}] - {today}\n\n{body}\n\n"
73
+ )
74
+ updated = text[:match.start()] + new_block + text[end:].lstrip()
75
+ path.write_text(updated, encoding="utf-8")
76
+ Path("PROMOTED.flag").write_text("promoted", encoding="utf-8")
77
+ print(f"Promoted [Unreleased] -> [{version}] - {today}")
78
+ PY
79
+ if [ -f PROMOTED.flag ]; then
80
+ echo "result=$(cat PROMOTED.flag)" >> "$GITHUB_OUTPUT"
81
+ rm PROMOTED.flag
82
+ else
83
+ echo "result=skipped" >> "$GITHUB_OUTPUT"
84
+ fi
85
+
86
+ - name: Open PR with the promoted CHANGELOG
87
+ if: steps.promote.outputs.result == 'promoted'
88
+ uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
89
+ with:
90
+ commit-message: |
91
+ docs(changelog): promote [Unreleased] to [${{ github.event.release.tag_name }}]
92
+ title: "docs(changelog): promote [Unreleased] to [${{ github.event.release.tag_name }}]"
93
+ body: |
94
+ Automated promotion triggered by the `${{ github.event.release.tag_name }}` GitHub release.
95
+ Moves the `[Unreleased]` block under a new `[${{ github.event.release.tag_name }}]` heading dated today.
96
+ branch: changelog/promote-${{ github.event.release.tag_name }}
97
+ base: main
98
+ labels: docs, automation
@@ -0,0 +1,130 @@
1
+ name: Release
2
+
3
+ # Publishes to PyPI and attests build provenance when an immutable version tag
4
+ # is pushed. The v*.*.* filter matches patch tags (v1.2.3) but not the moving
5
+ # major tag (v1), so the moving tag does not trigger a duplicate publish.
6
+ on:
7
+ push:
8
+ tags:
9
+ - 'v*.*.*'
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ quality:
16
+ uses: ./.github/workflows/ci.yml
17
+
18
+ release:
19
+ needs: quality
20
+ if: vars.TRACEMANTLE_PUBLISH_ENABLED == 'true'
21
+ runs-on: ubuntu-latest
22
+ # Trusted publishing resolves against this environment on the PyPI side.
23
+ environment: pypi
24
+ permissions:
25
+ contents: read
26
+ id-token: write # PyPI trusted publishing (OIDC) and provenance signing
27
+ attestations: write # actions/attest-build-provenance
28
+ steps:
29
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
30
+
31
+ - name: Set up Python
32
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
33
+ with:
34
+ python-version: "3.12"
35
+
36
+ # Runs before publishing so a CHANGELOG that release-notes.yml cannot
37
+ # promote stops the release instead of shipping unrecorded entries.
38
+ # See scripts/check_changelog_release.py for the two rejected states.
39
+ - name: Verify the CHANGELOG can be promoted for this tag
40
+ run: python3 scripts/check_changelog_release.py "${GITHUB_REF_NAME}"
41
+
42
+ - name: Download the exact quality-gated artifacts
43
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
44
+ with:
45
+ name: tracemantle-dist
46
+ path: dist
47
+
48
+ - name: Refuse the legacy distribution
49
+ run: |
50
+ python - <<'PYCODE'
51
+ import pathlib, zipfile
52
+ artifacts = sorted(pathlib.Path("dist").iterdir())
53
+ assert len(artifacts) == 2
54
+ assert all(p.name.startswith("tracemantle-") for p in artifacts)
55
+ for path in artifacts:
56
+ if path.suffix == ".whl":
57
+ with zipfile.ZipFile(path) as wheel:
58
+ metadata = next(n for n in wheel.namelist() if n.endswith(".dist-info/METADATA"))
59
+ assert "\nName: tracemantle\n" in "\n" + wheel.read(metadata).decode()
60
+ PYCODE
61
+
62
+ - name: Verify wheel installs and version matches the tag
63
+ run: |
64
+ set -euo pipefail
65
+ pip install -c constraints-dev.txt dist/tracemantle-*.whl
66
+ installed="$(tracemantle --version | awk '{print $NF}')"
67
+ tag="${GITHUB_REF_NAME#v}"
68
+ if [ "$installed" != "$tag" ]; then
69
+ echo "Built version '$installed' does not match tag '$tag'." >&2
70
+ echo "Bump the version in pyproject.toml and __init__.py before tagging." >&2
71
+ exit 1
72
+ fi
73
+
74
+ - name: Attest build provenance
75
+ uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
76
+ with:
77
+ subject-path: |
78
+ dist/tracemantle-*.whl
79
+ dist/tracemantle-*.tar.gz
80
+
81
+ - name: Publish to PyPI
82
+ # Pinned to a version tag, not a commit SHA: this action runs a Docker
83
+ # container whose image (ghcr.io/pypa/gh-action-pypi-publish) is tagged
84
+ # by release version, never by commit SHA. A SHA ref makes the inner
85
+ # pull request an image tag that does not exist (manifest unknown), and
86
+ # would pin nothing anyway since the image is fetched by tag regardless.
87
+ #
88
+ # Keep this current with the build backend. The step above installs
89
+ # hatchling with constraints, so the wheel carries the Metadata-Version
90
+ # that the selected hatchling emits. v1.12.4 (January 2025) bundles a twine that
91
+ # rejects Metadata-Version 2.5, which is what hatchling writes today,
92
+ # and it failed the 1.5.0 publish with "not a valid metadata version".
93
+ uses: pypa/gh-action-pypi-publish@v1.14.2
94
+
95
+ # After a TraceMantle release, Action users may select `@v1`, so the moving
96
+ # major tag has to follow every patch release. Moving it by hand was missed
97
+ # for v1.3.0 and v1.4.1, which left `@v1` resolving to v1.2.3 for two months
98
+ # while the action installs from its own checkout. This job runs only after a
99
+ # successful publish and holds the only `contents: write` scope in the file,
100
+ # so the publishing job above stays least-privilege.
101
+ move-major-tag:
102
+ needs: release
103
+ runs-on: ubuntu-latest
104
+ permissions:
105
+ contents: write
106
+ steps:
107
+ - name: Point the major tag at this release
108
+ env:
109
+ GH_TOKEN: ${{ github.token }}
110
+ TAG: ${{ github.ref_name }}
111
+ SHA: ${{ github.sha }}
112
+ run: |
113
+ set -euo pipefail
114
+ # Plain vN.N.N only. The `v*.*.*` trigger glob also matches prerelease
115
+ # tags such as v2.0.0-rc1, which must not move the stable major tag.
116
+ if ! printf '%s' "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
117
+ echo "Tag '$TAG' is not a plain vN.N.N release tag; major tag unchanged."
118
+ exit 0
119
+ fi
120
+ major="${TAG%%.*}"
121
+ echo "Moving '$major' to $SHA ($TAG)."
122
+ # PATCH updates the existing ref. POST creates it on the first release
123
+ # of a new major line, where the major tag does not exist yet.
124
+ if gh api "repos/${GITHUB_REPOSITORY}/git/refs/tags/${major}" >/dev/null 2>&1; then
125
+ gh api -X PATCH "repos/${GITHUB_REPOSITORY}/git/refs/tags/${major}" \
126
+ -f sha="$SHA" -F force=true
127
+ else
128
+ gh api -X POST "repos/${GITHUB_REPOSITORY}/git/refs" \
129
+ -f ref="refs/tags/${major}" -f sha="$SHA"
130
+ fi
@@ -0,0 +1,37 @@
1
+ .env
2
+ .DS_Store
3
+ __pycache__/
4
+ *.py[cod]
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .mypy_cache/
11
+ .coverage
12
+ .coverage.*
13
+ htmlcov/
14
+ *.egg
15
+ CLAUDE.md
16
+ # Field test artifacts (kept locally, not in git)
17
+ runs/
18
+
19
+ # Launch post drafts (kept locally, never push)
20
+ launch-post/
21
+ LAUNCH_CHECKLIST.md
22
+ LAUNCH_POST_*.md
23
+
24
+ # Per-skill history ledgers written by `tracemantle --history`; running the tool
25
+ # against a file in the repo drops one next to it and it must never be committed.
26
+ .skillcheck-history.json
27
+
28
+ # Verification venvs (created by end-to-end verification runs)
29
+ .venv-verify/
30
+
31
+ # Agent scratchpads (kept locally; never push)
32
+ .codex
33
+ review.md
34
+ .venv/
35
+
36
+ candidate-dist/
37
+ .tracemantle/
@@ -0,0 +1,7 @@
1
+ - id: tracemantle
2
+ name: tracemantle
3
+ entry: tracemantle
4
+ args: ["--no-color"]
5
+ language: python
6
+ files: '(^|/)SKILL\.md$'
7
+ pass_filenames: true