narrativetrace 0.1.2__tar.gz → 0.3.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 (280) hide show
  1. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/.gitignore +1 -0
  2. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/LICENSE +2 -2
  3. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/PKG-INFO +4 -2
  4. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/README.md +2 -1
  5. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/pyproject.toml +14 -3
  6. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/__init__.py +1 -1
  7. narrativetrace-0.3.0/src/narrativetrace/_skills/agents/add-narrative-tracing/SKILL.md +178 -0
  8. narrativetrace-0.3.0/src/narrativetrace/_skills/agents/add-narrativetrace-clarity/SKILL.md +106 -0
  9. narrativetrace-0.3.0/src/narrativetrace/_skills/agents/narrativetrace-debug/SKILL.md +189 -0
  10. narrativetrace-0.3.0/src/narrativetrace/_skills/agents/narrativetrace-doctor/SKILL.md +89 -0
  11. narrativetrace-0.3.0/src/narrativetrace/_skills/agents/narrativetrace-feedback/SKILL.md +71 -0
  12. narrativetrace-0.3.0/src/narrativetrace/_skills/agents/narrativetrace-verify/SKILL.md +165 -0
  13. narrativetrace-0.3.0/src/narrativetrace/_skills/catalogue.json +41 -0
  14. narrativetrace-0.3.0/src/narrativetrace/_skills/claude/add-narrative-tracing/SKILL.md +180 -0
  15. narrativetrace-0.3.0/src/narrativetrace/_skills/claude/add-narrativetrace-clarity/SKILL.md +108 -0
  16. narrativetrace-0.3.0/src/narrativetrace/_skills/claude/narrativetrace-debug/SKILL.md +190 -0
  17. narrativetrace-0.3.0/src/narrativetrace/_skills/claude/narrativetrace-doctor/SKILL.md +91 -0
  18. narrativetrace-0.3.0/src/narrativetrace/_skills/claude/narrativetrace-feedback/SKILL.md +72 -0
  19. narrativetrace-0.3.0/src/narrativetrace/_skills/claude/narrativetrace-verify/SKILL.md +166 -0
  20. narrativetrace-0.3.0/src/narrativetrace/doctor/__init__.py +27 -0
  21. narrativetrace-0.3.0/src/narrativetrace/doctor/cli_bin.py +482 -0
  22. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/doctor/environment.py +47 -7
  23. narrativetrace-0.3.0/src/narrativetrace/doctor/feedback_cli.py +350 -0
  24. narrativetrace-0.3.0/src/narrativetrace/doctor/gh_auth_probe.py +105 -0
  25. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/escape.py +14 -5
  26. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/logging_bridge.py +2 -2
  27. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/national_id_shapes.py +1 -1
  28. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/approval.py +9 -9
  29. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/line_diff.py +54 -17
  30. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/structural_delta.py +39 -15
  31. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/writer.py +16 -22
  32. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/__init__.py +2 -3
  33. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/redacted_paths.py +15 -18
  34. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/concurrency.py +16 -1
  35. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/indented.py +65 -46
  36. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/markdown.py +68 -40
  37. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/prose.py +45 -21
  38. narrativetrace-0.3.0/src/narrativetrace/render/span_id.py +139 -0
  39. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/structural.py +50 -23
  40. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/value_delta.py +1 -1
  41. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/rendering.py +387 -85
  42. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/secret_value_shapes.py +2 -1
  43. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/signature.py +2 -2
  44. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/span.py +2 -2
  45. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/template.py +67 -33
  46. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/trace_object.py +10 -3
  47. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/tree_walk.py +2 -2
  48. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/conftest.py +21 -1
  49. narrativetrace-0.3.0/tests/doctor/test_adversarial_feedback_cli.py +149 -0
  50. narrativetrace-0.3.0/tests/doctor/test_cli_built_wheels.py +240 -0
  51. narrativetrace-0.3.0/tests/doctor/test_cli_feedback_arguments.py +162 -0
  52. narrativetrace-0.3.0/tests/doctor/test_cli_feedback_verb.py +505 -0
  53. narrativetrace-0.3.0/tests/doctor/test_cli_installer_arguments.py +226 -0
  54. narrativetrace-0.3.0/tests/doctor/test_cli_installer_verbs.py +796 -0
  55. narrativetrace-0.3.0/tests/doctor/test_doctor_cli_bin.py +181 -0
  56. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/doctor/test_doctor_environment.py +99 -13
  57. narrativetrace-0.3.0/tests/doctor/test_gh_auth_probe.py +115 -0
  58. narrativetrace-0.3.0/tests/fixtures/java-negative-expense.nt +8 -0
  59. narrativetrace-0.3.0/tests/logging_wiring.py +40 -0
  60. narrativetrace-0.3.0/tests/test_adversarial_skills_render.py +123 -0
  61. narrativetrace-0.3.0/tests/test_adversarial_vendor_validation.py +244 -0
  62. narrativetrace-0.3.0/tests/test_adversarial_verify_all_heavy.py +134 -0
  63. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_approval.py +27 -0
  64. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_chapter.py +6 -0
  65. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_check_no_license_headers.py +48 -23
  66. narrativetrace-0.3.0/tests/test_comment_hygiene.py +175 -0
  67. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_context_extra.py +8 -0
  68. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_contract_check.py +2 -3
  69. narrativetrace-0.3.0/tests/test_contract_dispatch_coverage.py +70 -0
  70. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_contract_lint.py +14 -65
  71. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_coverage_source_completeness.py +3 -2
  72. narrativetrace-0.3.0/tests/test_deep_fixture_budget.py +251 -0
  73. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_distribution_licensing.py +51 -7
  74. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_escape.py +15 -0
  75. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_export.py +8 -0
  76. narrativetrace-0.3.0/tests/test_feedback_docs.py +81 -0
  77. narrativetrace-0.3.0/tests/test_framework_table_docs.py +131 -0
  78. narrativetrace-0.3.0/tests/test_installer_from_another_package.py +331 -0
  79. narrativetrace-0.3.0/tests/test_issue_form_publishes.py +194 -0
  80. narrativetrace-0.3.0/tests/test_logging_wiring.py +28 -0
  81. narrativetrace-0.3.0/tests/test_mutation_baseline_selection.py +57 -0
  82. narrativetrace-0.3.0/tests/test_mutation_workers.py +135 -0
  83. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_no_poison_contract.py +28 -6
  84. narrativetrace-0.3.0/tests/test_notice_render_targets.py +39 -0
  85. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_output.py +8 -0
  86. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_publish_public.py +105 -0
  87. narrativetrace-0.3.0/tests/test_publish_public_host_venv.py +134 -0
  88. narrativetrace-0.3.0/tests/test_publish_workflow_packages.py +50 -0
  89. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_redacted_paths.py +22 -7
  90. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_render.py +29 -12
  91. narrativetrace-0.3.0/tests/test_render_reads_state.py +544 -0
  92. narrativetrace-0.3.0/tests/test_render_reentrancy.py +264 -0
  93. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_rendering.py +92 -32
  94. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_rendering_extra.py +23 -5
  95. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_run_security_tool.py +4 -4
  96. narrativetrace-0.3.0/tests/test_skills_carrier_wheel_contents.py +121 -0
  97. narrativetrace-0.3.0/tests/test_skills_render.py +147 -0
  98. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_smoke.py +1 -1
  99. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_snippet_check.py +107 -7
  100. narrativetrace-0.3.0/tests/test_span_id.py +120 -0
  101. narrativetrace-0.3.0/tests/test_span_id_flavours.py +197 -0
  102. narrativetrace-0.3.0/tests/test_span_id_props.py +167 -0
  103. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_structural.py +125 -13
  104. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_structural_conformance.py +54 -0
  105. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_structural_delta.py +106 -0
  106. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_template.py +71 -33
  107. narrativetrace-0.3.0/tests/test_timeout_floor.py +167 -0
  108. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_trace_object.py +30 -4
  109. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_tree.py +15 -0
  110. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_tree_canonical.py +6 -0
  111. narrativetrace-0.3.0/tests/test_tree_writes_guard.py +407 -0
  112. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_value_reference.py +8 -0
  113. narrativetrace-0.3.0/tests/test_vendor_validation.py +274 -0
  114. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all.py +2 -2
  115. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all_heavy.py +42 -4
  116. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all_security.py +85 -1
  117. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_publication_registry.py +57 -0
  118. narrativetrace-0.3.0/tests/test_version_literals.py +419 -0
  119. narrativetrace-0.3.0/tests/test_version_literals_props.py +70 -0
  120. narrativetrace-0.3.0/tests/wheel_support.py +86 -0
  121. narrativetrace-0.1.2/src/narrativetrace/doctor/__init__.py +0 -22
  122. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/approval_traces.py +0 -37
  123. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/llms_before_you_start.py +0 -46
  124. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/not_traced_unused.py +0 -64
  125. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/output_env.py +0 -52
  126. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/package_versions.py +0 -51
  127. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/parameter_names.py +0 -46
  128. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/pytest_plugin_registered.py +0 -82
  129. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/pytest_version.py +0 -50
  130. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/python_version.py +0 -38
  131. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/redaction_proof.py +0 -40
  132. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/unknown_config_keys.py +0 -51
  133. narrativetrace-0.1.2/src/narrativetrace/doctor/cli_bin.py +0 -115
  134. narrativetrace-0.1.2/src/narrativetrace/doctor/doc_urls.py +0 -31
  135. narrativetrace-0.1.2/src/narrativetrace/doctor/doctor.py +0 -43
  136. narrativetrace-0.1.2/src/narrativetrace/doctor/finding.py +0 -21
  137. narrativetrace-0.1.2/src/narrativetrace/doctor/render.py +0 -48
  138. narrativetrace-0.1.2/src/narrativetrace/doctor/types.py +0 -89
  139. narrativetrace-0.1.2/src/narrativetrace/doctor/version_lite.py +0 -91
  140. narrativetrace-0.1.2/tests/doctor/conftest.py +0 -58
  141. narrativetrace-0.1.2/tests/doctor/snapshot_factory_types.py +0 -18
  142. narrativetrace-0.1.2/tests/doctor/test_check_approval_traces.py +0 -48
  143. narrativetrace-0.1.2/tests/doctor/test_check_llms_before_you_start.py +0 -36
  144. narrativetrace-0.1.2/tests/doctor/test_check_not_traced_unused.py +0 -60
  145. narrativetrace-0.1.2/tests/doctor/test_check_output_env.py +0 -48
  146. narrativetrace-0.1.2/tests/doctor/test_check_package_versions.py +0 -59
  147. narrativetrace-0.1.2/tests/doctor/test_check_parameter_names.py +0 -29
  148. narrativetrace-0.1.2/tests/doctor/test_check_pytest_plugin_registered.py +0 -80
  149. narrativetrace-0.1.2/tests/doctor/test_check_pytest_version.py +0 -59
  150. narrativetrace-0.1.2/tests/doctor/test_check_python_version.py +0 -47
  151. narrativetrace-0.1.2/tests/doctor/test_check_redaction_proof.py +0 -46
  152. narrativetrace-0.1.2/tests/doctor/test_check_unknown_config_keys.py +0 -31
  153. narrativetrace-0.1.2/tests/doctor/test_doctor_cli_bin.py +0 -130
  154. narrativetrace-0.1.2/tests/doctor/test_doctor_finding.py +0 -33
  155. narrativetrace-0.1.2/tests/doctor/test_doctor_registry.py +0 -51
  156. narrativetrace-0.1.2/tests/doctor/test_doctor_render.py +0 -53
  157. narrativetrace-0.1.2/tests/doctor/test_doctor_version_lite.py +0 -81
  158. narrativetrace-0.1.2/tests/fixtures/java-negative-expense.nt +0 -8
  159. narrativetrace-0.1.2/tests/test_llms_banner.py +0 -325
  160. narrativetrace-0.1.2/tests/test_skills_render.py +0 -45
  161. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/_boundary.py +0 -0
  162. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/canonical.py +0 -0
  163. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/chapter.py +0 -0
  164. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/cli.py +0 -0
  165. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/concurrency.py +0 -0
  166. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/config.py +0 -0
  167. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/context.py +0 -0
  168. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/context_export.py +0 -0
  169. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/decorators.py +0 -0
  170. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/events.py +0 -0
  171. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/export.py +0 -0
  172. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/groups.py +0 -0
  173. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/identity.py +0 -0
  174. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/ids.py +0 -0
  175. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/levels.py +0 -0
  176. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/loss.py +0 -0
  177. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/markers.py +0 -0
  178. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/metadata.py +0 -0
  179. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/namer.py +0 -0
  180. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/nodes.py +0 -0
  181. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/outcomes.py +0 -0
  182. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/__init__.py +0 -0
  183. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/artifact_identity.py +0 -0
  184. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/manifest.py +0 -0
  185. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/paths.py +0 -0
  186. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/reporter.py +0 -0
  187. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/run_identity.py +0 -0
  188. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/output/warnings.py +0 -0
  189. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/bounded_buffer.py +0 -0
  190. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/buffered_consumer.py +0 -0
  191. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/dual_path.py +0 -0
  192. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/event_store.py +0 -0
  193. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/perishable.py +0 -0
  194. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/subscriber.py +0 -0
  195. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/py.typed +0 -0
  196. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/redaction.py +0 -0
  197. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/__init__.py +0 -0
  198. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/base.py +0 -0
  199. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/camel.py +0 -0
  200. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/frontmatter.py +0 -0
  201. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/scenario.py +0 -0
  202. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/scenario_result.py +0 -0
  203. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/render/value_reference.py +0 -0
  204. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/tree.py +0 -0
  205. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/tree_canonical.py +0 -0
  206. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/src/narrativetrace/values.py +0 -0
  207. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/live_annotation_support.py +0 -0
  208. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/stress_support.py +0 -0
  209. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_artifact_identity.py +0 -0
  210. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_async_concurrency_kind.py +0 -0
  211. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_async_lifecycle_contract.py +0 -0
  212. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_async_snapshot_adoption.py +0 -0
  213. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_bench_capture.py +0 -0
  214. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_bench_redaction.py +0 -0
  215. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_bench_rendering.py +0 -0
  216. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_canonical.py +0 -0
  217. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_canonical_props.py +0 -0
  218. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_capture_identity.py +0 -0
  219. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_chapter_props.py +0 -0
  220. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_cli.py +0 -0
  221. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_concurrency_info.py +0 -0
  222. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_config.py +0 -0
  223. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_console_delta.py +0 -0
  224. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_context.py +0 -0
  225. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_context_export.py +0 -0
  226. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_context_stress.py +0 -0
  227. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_decorators.py +0 -0
  228. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_duplication_check.py +0 -0
  229. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_duplication_report.py +0 -0
  230. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_events.py +0 -0
  231. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_fuzz_report.py +0 -0
  232. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_groups.py +0 -0
  233. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_identity.py +0 -0
  234. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_identity_conformance.py +0 -0
  235. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_ids.py +0 -0
  236. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_levels.py +0 -0
  237. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_live_snapshot_scope_visibility.py +0 -0
  238. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_logging_bridge.py +0 -0
  239. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_manifest.py +0 -0
  240. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_metadata.py +0 -0
  241. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_mutation_accounting.py +0 -0
  242. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_mutation_gate.py +0 -0
  243. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_mutation_tooling.py +0 -0
  244. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_namer.py +0 -0
  245. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_national_id_shapes.py +0 -0
  246. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_nested_chain_adoption.py +0 -0
  247. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_nodes.py +0 -0
  248. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_outcomes.py +0 -0
  249. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_parameter_name_redaction.py +0 -0
  250. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_pipeline_buffered.py +0 -0
  251. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_pipeline_dual_path.py +0 -0
  252. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_pipeline_perishable.py +0 -0
  253. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_pipeline_props.py +0 -0
  254. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_pipeline_stress.py +0 -0
  255. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_pipeline_subscriber.py +0 -0
  256. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_publish_agents_md_section.py +0 -0
  257. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_quality_gate_status.py +0 -0
  258. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_redacted_value_containment.py +0 -0
  259. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_redaction.py +0 -0
  260. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_run_identity.py +0 -0
  261. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_scenario_result.py +0 -0
  262. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_secret_value_shapes.py +0 -0
  263. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_signature.py +0 -0
  264. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_span.py +0 -0
  265. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_structlog_dependency.py +0 -0
  266. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_trace_loss.py +0 -0
  267. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_trace_stack_live_child.py +0 -0
  268. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_translation_check.py +0 -0
  269. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_tree_props.py +0 -0
  270. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_tree_walk.py +0 -0
  271. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_value_delta.py +0 -0
  272. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_values.py +0 -0
  273. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all_arch_stress.py +0 -0
  274. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all_exec.py +0 -0
  275. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all_schema.py +0 -0
  276. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all_static.py +0 -0
  277. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_all_testrun.py +0 -0
  278. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_publication_packages.py +0 -0
  279. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_verify_publication_provenance.py +0 -0
  280. {narrativetrace-0.1.2 → narrativetrace-0.3.0}/tests/test_writer_structural.py +0 -0
@@ -1,6 +1,7 @@
1
1
  __pycache__/
2
2
  *.py[cod]
3
3
  .venv/
4
+ .venv-host/
4
5
  .pytest_cache/
5
6
  .mypy_cache/
6
7
  .ruff_cache/
@@ -7,7 +7,7 @@ Parameters
7
7
 
8
8
  Licensor: Empower Agile
9
9
 
10
- Licensed Work: NarrativeTrace for Python (the narrativetrace PyPI distributions) version 0.1.2. The Licensed Work is (c) 2026 Empower Agile.
10
+ Licensed Work: NarrativeTrace for Python (the narrativetrace PyPI distributions) version 0.3.0. The Licensed Work is (c) 2026 Empower Agile.
11
11
 
12
12
  Additional Use Grant: You may make production use of the Licensed Work for any
13
13
  purpose, including internal use and use in products and
@@ -18,7 +18,7 @@ Additional Use Grant: You may make production use of the Licensed Work for any
18
18
  parties as a logging, tracing, or code-narrative product
19
19
  or service.
20
20
 
21
- Change Date: Four years from the date the Licensed Work is published. (2030-09-16 for this version)
21
+ Change Date: Four years from the date the Licensed Work is published. (2030-10-10 for this version)
22
22
 
23
23
  Change License: Apache License, Version 2.0
24
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: narrativetrace
3
- Version: 0.1.2
3
+ Version: 0.3.0
4
4
  Summary: Narrative execution tracing for Python — human- and LLM-readable traces
5
5
  Project-URL: Homepage, https://github.com/narrativetrace/narrativetrace-python
6
6
  Project-URL: Source, https://github.com/narrativetrace/narrativetrace-python
@@ -12,11 +12,13 @@ Classifier: Development Status :: 3 - Alpha
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Typing :: Typed
14
14
  Requires-Python: >=3.12
15
+ Requires-Dist: narrativetrace-tooling
15
16
  Description-Content-Type: text/markdown
16
17
 
17
18
  # narrativetrace
18
19
 
19
- Human- and LLM-readable narrative execution tracing for Python. Dependency-free core.
20
+ Human- and LLM-readable narrative execution tracing for Python. The core, with no third-party
21
+ dependencies.
20
22
 
21
23
  Part of the [narrativetrace](../../README.md) workspace. The
22
24
  [Feature Guide](../../documentation/feature-guide.md) carries scope and status.
@@ -1,6 +1,7 @@
1
1
  # narrativetrace
2
2
 
3
- Human- and LLM-readable narrative execution tracing for Python. Dependency-free core.
3
+ Human- and LLM-readable narrative execution tracing for Python. The core, with no third-party
4
+ dependencies.
4
5
 
5
6
  Part of the [narrativetrace](../../README.md) workspace. The
6
7
  [Feature Guide](../../documentation/feature-guide.md) carries scope and status.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "narrativetrace"
3
- version = "0.1.2"
3
+ version = "0.3.0"
4
4
  description = "Narrative execution tracing for Python — human- and LLM-readable traces"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -16,7 +16,11 @@ classifiers = [
16
16
  "Programming Language :: Python :: 3.12",
17
17
  "Typing :: Typed",
18
18
  ]
19
- dependencies = []
19
+ # One dependency, and it carries no further ones: narrativetrace-tooling is this family's own
20
+ # standard-library-only library (its architecture test pins that), holding the doctor's checks and
21
+ # the agent-skills installer behind the `narrativetrace` console script declared below. No
22
+ # third-party code reaches a consumer's environment through this distribution.
23
+ dependencies = ["narrativetrace-tooling"]
20
24
 
21
25
  [project.urls]
22
26
  Homepage = "https://github.com/narrativetrace/narrativetrace-python"
@@ -41,7 +45,14 @@ packages = ["src/narrativetrace"]
41
45
  [tool.mutmut]
42
46
  source_paths = ["src/narrativetrace/"]
43
47
  also_copy = ["tests/"]
44
- pytest_add_cli_args_test_selection = ["tests/"]
48
+ # "not distribution" deselects the tests marked `distribution`: they build real wheels/sdists via
49
+ # `uv build`, which an offline mutation run cannot do once its build backend is not already
50
+ # cached -- a distribution test proves packaging, not code under mutation, and has no place in
51
+ # this baseline (it still runs in the ordinary test suite). "not stress" is restated here, not
52
+ # just "not distribution" added, because pytest keeps only the LAST -m it sees rather than
53
+ # combining several -m flags -- a bare "not distribution" here would silently override the root
54
+ # addopts' own "not stress" and let the concurrency stress suite back into every mutation run.
55
+ pytest_add_cli_args_test_selection = ["tests/", "-m", "not stress and not distribution"]
45
56
  # The plugin distribution imports narrativetrace at plugin-load time, before the mutated copy is on
46
57
  # sys.path — loading it would test the installed package instead of the mutant.
47
58
  pytest_add_cli_args = ["-p", "no:narrativetrace"]
@@ -97,7 +97,7 @@ from narrativetrace.values import (
97
97
  StringVal,
98
98
  )
99
99
 
100
- __version__ = "0.1.2"
100
+ __version__ = "0.3.0"
101
101
 
102
102
  __all__ = [
103
103
  "NOOP_CONTEXT",
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: add-narrative-tracing
3
+ description: "Installs NarrativeTrace into a Python project and gets it to a first trace. Use when NarrativeTrace is not yet installed, a project needs its very first traced call, or traces need to reach a real logger instead of bare print statements. Installs narrativetrace with uv add, wraps an object with trace_object, renders and runs the first trace, then wires the stdlib logging bridge so traces reach your logger. Applies the doctor's framework-wiring fixes for the frameworks the project already uses, and runs narrativetrace doctor to confirm the install is correctly wired -- narrativetrace-doctor owns diagnosis from there -- and previews the agent-skills install so the next session finds them. Say 'add narrative tracing to my service', 'install narrativetrace', 'get a trace in 60 seconds', 'wrap this object so I can see a trace', or 'send my traces to my logger' to invoke it."
4
+ ---
5
+
6
+ # add-narrative-tracing
7
+
8
+ ## 1. Install with the real toolchain
9
+
10
+ ```bash
11
+ uv sync --all-packages
12
+ ```
13
+
14
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
15
+ report = json.load(sys.stdin)
16
+ bad = [f for f in report["findings"] if f["id"].startswith("toolchain.") and f["status"] != "pass"]
17
+ if bad:
18
+ print(json.dumps(bad))
19
+ sys.exit(1 if bad else 0)
20
+ '`
21
+
22
+ **failure:** a sibling narrativetrace-* package disagrees on version — an installed distribution outside the lockstep version the other narrativetrace-* packages share. Fix: run the narrativetrace-doctor skill's toolchain.package-versions check, then pin every narrativetrace-* dependency to the same version and `uv sync`
23
+
24
+ ## 2. Wire the frameworks this project already uses
25
+
26
+ ```bash
27
+ uv run narrativetrace doctor || true
28
+ ```
29
+
30
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
31
+ from narrativetrace_tooling.frameworks.table import wiring_check_ids
32
+ report = json.load(sys.stdin)
33
+ ids = set(wiring_check_ids())
34
+ bad = [f for f in report["findings"] if f["id"] in ids and f["status"] != "pass"]
35
+ for f in bad:
36
+ print(f["id"] + ": " + f["fix"])
37
+ sys.exit(1 if bad else 0)
38
+ '`
39
+
40
+ **failure:** a config.<framework>-* finding fails — the doctor detected a framework this project uses whose NarrativeTrace integration is not added, or is added but never wired. Fix: run the doctor; apply every config.<framework>-* fix it prints, in order; a framework it reports as having no integration shipped is left alone
41
+
42
+ ## 3. First trace: wrap, call, render, run
43
+
44
+ **when:** if the project already has an application entry point — a script or module that starts it, a web or application framework the doctor reports — do not create a demo main.py: run the application the way it already runs, exercise one real boundary, and read that request's trace; the verify below is for the standalone script, and in an existing application the step is done when that request's trace is in the output; otherwise create the smallest script as follows
45
+
46
+ <!-- snippet: examples/sixty_seconds/main.py -->
47
+ ```python
48
+ # main.py
49
+ from narrativetrace import ContextVarNarrativeContext, IndentedTextRenderer, TraceId, trace_object
50
+
51
+
52
+ class OrderService:
53
+ def place_order(self, customer_id, product_id, quantity):
54
+ return f"ORD-{customer_id}-{product_id}-{quantity}"
55
+
56
+
57
+ # snippet:begin fixedTraceId
58
+ # A fixed trace id, adopted so this page's embedded output always names the same trace. A real
59
+ # run generates a random one every time (never this -- it is this DEMO's own constant, not the
60
+ # library default) via the same TraceId.adopt_trace_id a servlet-style boundary uses for an
61
+ # inbound trace header.
62
+ DEMO_TRACE_ID = TraceId("a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4")
63
+
64
+ # snippet:end fixedTraceId
65
+
66
+ context = ContextVarNarrativeContext()
67
+ context.adopt_trace_id(DEMO_TRACE_ID)
68
+ service = trace_object(OrderService(), context)
69
+ service.place_order("cust-1", "prod-42", 3)
70
+
71
+ print(IndentedTextRenderer().render(context.capture_trace()))
72
+ ```
73
+ <!-- /snippet -->
74
+
75
+ **verify:** `uv run python main.py`
76
+
77
+ **failure:** a *args method's parameters render as one args: [...] value — inspect.signature has nothing named to reconstruct per-argument that Python itself does not have. Fix: supply explicit names: @traced("first", "second", ...) above the method
78
+
79
+ ## 4. Send it to your logger
80
+
81
+ **when:** if the project already has an application entry point — a script or module that starts it, a web or application framework the doctor reports — do not create a second main.py or a second logging setup: add the filter and the export_to_logger call to the application's own logging configuration and the boundary you exercised; the verify below is for the standalone script, and in an existing application the step is done when that boundary's trace reaches the application's own logger; otherwise create the smallest script as follows
82
+
83
+ <!-- snippet: examples/sixty_seconds/main_with_logger.py -->
84
+ ```python
85
+ # main.py
86
+ import logging # new: stdlib logging -- the sink this step sends the trace to
87
+ import sys # new: stdout target for the handler below
88
+
89
+ from narrativetrace import (
90
+ ContextVarNarrativeContext,
91
+ IndentedTextRenderer,
92
+ NarrativeContextFilter, # new: injects traceName/runName onto every log record
93
+ TraceId,
94
+ export_to_logger, # new: replays an already-captured trace through your logger, one call
95
+ trace_object,
96
+ )
97
+
98
+
99
+ class OrderService:
100
+ def place_order(self, customer_id, product_id, quantity):
101
+ return f"ORD-{customer_id}-{product_id}-{quantity}"
102
+
103
+
104
+ # snippet:begin fixedTraceId
105
+ # A fixed trace id, adopted so this page's embedded output always names the same trace. A real
106
+ # run generates a random one every time (never this -- it is this DEMO's own constant, not the
107
+ # library default) via the same TraceId.adopt_trace_id a servlet-style boundary uses for an
108
+ # inbound trace header.
109
+ DEMO_TRACE_ID = TraceId("a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4")
110
+
111
+ # snippet:end fixedTraceId
112
+
113
+ # new: a plain stdlib logging setup -- the shape a real app's own logging config already has
114
+ handler = logging.StreamHandler(sys.stdout)
115
+ handler.addFilter(NarrativeContextFilter()) # new: makes traceName/runName available below
116
+ # new: DEBUG so export_to_logger's records pass the handler; the format reads the filter's keys
117
+ logging.basicConfig(
118
+ level=logging.DEBUG, format="[%(traceName)s] [%(runName)s] %(message)s", handlers=[handler]
119
+ )
120
+
121
+ context = ContextVarNarrativeContext()
122
+ context.adopt_trace_id(DEMO_TRACE_ID)
123
+ service = trace_object(OrderService(), context)
124
+ service.place_order("cust-1", "prod-42", 3)
125
+
126
+ trace = context.capture_trace() # new: capture once, reuse for both the print and the export
127
+ print(IndentedTextRenderer().render(trace))
128
+
129
+ export_to_logger(trace) # new: sends the same captured trace through the configured logger
130
+ ```
131
+ <!-- /snippet -->
132
+
133
+ **verify:** `uv run python main_with_logger.py`
134
+
135
+ ## 5. Run the doctor and resolve its findings
136
+
137
+ ```bash
138
+ uv run narrativetrace doctor || true
139
+ ```
140
+
141
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
142
+ report = json.load(sys.stdin)
143
+ findings = report.get("findings")
144
+ sys.exit(1 if not isinstance(findings, list) or len(findings) != 19 else 0)
145
+ '`
146
+
147
+ ## 6. Install the skills for next time
148
+
149
+ ```bash
150
+ uv run narrativetrace init --dry-run
151
+ ```
152
+
153
+ **done when:** the preview wrote nothing; the next session verifies with narrativetrace-verify — once a change's tests are green, it reads the trace before it reports
154
+
155
+ **verify:** `uv run python -c 'import pathlib, subprocess, sys
156
+ result = subprocess.run(
157
+ ["uv", "run", "narrativetrace", "init", "--dry-run"], capture_output=True, text=True
158
+ )
159
+ shown = (
160
+ "action(s)" in result.stdout
161
+ and ".agents/skills" in result.stdout
162
+ and "AGENTS.md" in result.stdout
163
+ )
164
+ untouched = not pathlib.Path(".agents/skills").exists() and not pathlib.Path("AGENTS.md").exists()
165
+ sys.exit(0 if result.returncode == 0 and shown and untouched else 1)
166
+ '`
167
+
168
+ **failure:** the command exits 1 with no carrier resolved — no narrativetrace-skills distribution is installed and this project's own narrativetrace bundles no reachable fallback. Fix: run `uv add narrativetrace-skills`, or point --from at a local carrier directory or wheel
169
+
170
+ ## Always
171
+
172
+ - Reinstall clean (`uv sync`) rather than trusting whatever is already in the virtual environment. (a mismatched sibling version or a stale lockfile is the single most common install failure, and it only surfaces on a clean install)
173
+
174
+ ## Never
175
+
176
+ - Never assume a step worked without running its verify. (self-reported success overstates reality -- a build claimed green that does not reproduce from clean is not evidence)
177
+ - Never skip the final narrativetrace doctor call. (it is the seam that catches anything these five steps did not -- narrativetrace-doctor owns diagnosis from here)
178
+ - Never apply the installer without showing its diff first. (it writes into AGENTS.md and the project's skill directories, and the approval for that is a person reading the diff -- run it with --dry-run, show the output, and let them run it again without the flag)
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: add-narrativetrace-clarity
3
+ description: "Adds or verifies NarrativeTrace Clarity in a Python project. Use when you want a first naming report, per-element suggestions, or an explicit clarity quality gate. Installs narrativetrace-clarity with uv add --dev, runs the real narrativetrace-clarity scan over the project's packages, checks the report is fresh and scored at least one class, renames what it flags, and re-runs until the gate is clean, preserving any thresholds the project already has. Route tracing itself being broken to narrativetrace-doctor. Say 'add a clarity report to this project', 'run narrativetrace-clarity', 'explain our clarity scores', 'make the clarity gate fail on low scores', or 'which names make this hard to read' to invoke it."
4
+ ---
5
+
6
+ # add-narrativetrace-clarity
7
+
8
+ ## 1. Find what to scan and what is already there
9
+
10
+ ```bash
11
+ git ls-files '*.py' 'pyproject.toml'
12
+ ```
13
+
14
+ **verify:** `git ls-files '*.py' 'pyproject.toml' | uv run python -c 'import sys
15
+ sys.exit(0 if sys.stdin.read().split() else 1)
16
+ '`
17
+
18
+ **failure:** the listing is empty — this is not a git checkout, or it tracks no Python. Fix: run from the repository root; with no Python to read there is nothing for clarity to score
19
+
20
+ ## 2. Install the gate as a development dependency
21
+
22
+ ```bash
23
+ uv add --dev narrativetrace-clarity
24
+ ```
25
+
26
+ **verify:** `uv run narrativetrace-clarity --help`
27
+
28
+ **failure:** the console script is not found — the dev dependency was added but the environment was not synced. Fix: run `uv sync`, then run the check again
29
+
30
+ ## 3. Scan the packages and check the report is real
31
+
32
+ ```bash
33
+ uv run narrativetrace-clarity <source-dir> --output-dir build/narrativetrace
34
+ ```
35
+
36
+ **verify:** `uv run python -c 'import json, pathlib, sys, time
37
+ root = pathlib.Path("build/narrativetrace")
38
+ results = root / "clarity-results.json"
39
+ report = root / ("clarity-report" + ".md")
40
+ if not results.is_file() or not report.is_file() or not report.stat().st_size:
41
+ sys.exit("no report: the scan wrote nothing (wrong source directory?)")
42
+ if time.time() - min(results.stat().st_mtime, report.stat().st_mtime) > 600:
43
+ sys.exit("stale report: it is from an earlier run, not this scan")
44
+ try:
45
+ scenarios = json.loads(results.read_text(encoding="utf-8")).get("scenarios")
46
+ except (ValueError, AttributeError):
47
+ sys.exit("the results file is not a clarity report")
48
+ if not isinstance(scenarios, list) or not scenarios:
49
+ sys.exit("the report scored no class")
50
+ '`
51
+
52
+ **failure:** the scan prints `No classes found` and the check says no report — the directory holds no public class: the scan scores classes, skips names with a leading underscore, and does not score module-level functions. Fix: point it at the package directory that holds the project's classes
53
+
54
+ **failure:** the scores describe libraries, not the project — the scan walked the project root and so the virtual environment under it. Fix: pass the package directories, never `.`
55
+
56
+ ## 4. Read the report and rename what it flags
57
+
58
+ **failure:** a flagged name is a domain word the project chose on purpose — the scan scores against its built-in dictionaries unless a glossary is committed. Fix: leave the name and say why; the glossary rule below is the lasting fix
59
+
60
+ **failure:** a flagged method or parameter is part of a public API — renaming it breaks callers outside this repository. Fix: ask before renaming it, and keep the old name as a deprecated alias if so
61
+
62
+ ## 5. Run the project's tests after the renames
63
+
64
+ **verify:** `uv run pytest -q`
65
+
66
+ **failure:** a test fails with a name that no longer exists — a caller, a keyword argument or a string still uses the old name. Fix: search the whole repository for the old name and move every use
67
+
68
+ ## 6. Re-run the gate until it is clean
69
+
70
+ ```bash
71
+ uv run narrativetrace-clarity <source-dir> --output-dir build/narrativetrace --min-score <min-score> --max-high-issues <max-high-issues>
72
+ ```
73
+
74
+ **verify:** `uv run python -c 'import json, pathlib, sys, time
75
+ root = pathlib.Path("build/narrativetrace")
76
+ results = root / "clarity-results.json"
77
+ report = root / ("clarity-report" + ".md")
78
+ if not results.is_file() or not report.is_file() or not report.stat().st_size:
79
+ sys.exit("no report: the scan wrote nothing (wrong source directory?)")
80
+ if time.time() - min(results.stat().st_mtime, report.stat().st_mtime) > 600:
81
+ sys.exit("stale report: it is from an earlier run, not this scan")
82
+ try:
83
+ scenarios = json.loads(results.read_text(encoding="utf-8")).get("scenarios")
84
+ except (ValueError, AttributeError):
85
+ sys.exit("the results file is not a clarity report")
86
+ if not isinstance(scenarios, list) or not scenarios:
87
+ sys.exit("the report scored no class")
88
+ '`
89
+
90
+ **failure:** exit 1 naming a class below --min-score, or HIGH issues over the limit — a flagged name is still unclear, or a rename introduced another. Fix: read the new report, rename again, and repeat from the tests
91
+
92
+ ## Always
93
+
94
+ - Rename in snake_case, whatever case the suggestion's examples use. (the suggestion text shares its wording with the other runtimes, so its examples are camelCase; the idea is the lesson, not the spelling)
95
+ - Read both artefacts of every run, not only the exit code. (a scan that found no class exits 0 and leaves the last run's files in place)
96
+ - Use the thresholds the project already has, and ask before choosing new ones. (enforcement is a decision the project makes, not a default the skill invents)
97
+ - With no thresholds in the project and none requested, re-run the plain scan and report what is left. (a gate nobody asked for fails a build nobody expected to fail)
98
+ - When the project commits a glossary and has narrativetrace-glossary, run the scan through `python -m narrativetrace_glossary.clarity_scan` with the same arguments. (that is the gate that reads the committed vocabulary, so a domain word the project chose on purpose is not flagged as unclear)
99
+
100
+ ## Never
101
+
102
+ - Never lower or replace a threshold to hide a failure. (a failing gate is evidence about the names; removing the evidence fixes nothing)
103
+ - Never call a scan that found nothing a pass. (an exit 0 with no scored class means the wrong directory was scanned, not that the names are clear)
104
+ - Never add --warn-only to make the gate go green. (an advisory gate exits 0 whatever it found, so green then proves nothing)
105
+ - Never harvest a glossary only to read the existing vocabulary. (the scan reads a committed glossary; creating one is a separate decision that writes to the project)
106
+ - Never promise the score a rename will earn. (a rename's score is measured by the next run, and the report may flag more)
@@ -0,0 +1,189 @@
1
+ ---
2
+ name: narrativetrace-debug
3
+ description: "Finds the cause of a wrong result in a Python project by reading what the code did with the values, not by stepping through it. Use when a symptom is reported -- a wrong amount, a wrong id, a call in the wrong order, a test that fails with a value nobody expected. Reproduces it with the smallest input and NarrativeTrace on, finds the first span where a value diverges and names it by its span id (the sequence diagram first when threads or tasks are involved), narrows to that span's sub-tree, fixes it there and checks the structural trace shows nothing else moved, pins the reproduction as a regression test and an approval baseline behind your yes, and reports the root cause by span id. Hands a defect in NarrativeTrace itself to narrativetrace-feedback. Say 'debug this with the trace', 'find where this value goes wrong', or 'why is this result wrong' to invoke it."
4
+ ---
5
+
6
+ # narrativetrace-debug
7
+
8
+ ## 1. Reproduce the symptom with tracing on
9
+
10
+ **when:** a test already drives the path with the input from the symptom, run that one; otherwise write the smallest one, as below — the reported input, through the real collaborators each wrapped with trace_object and the narrative_trace fixture, asserting the value the symptom says should have come out
11
+
12
+ <!-- snippet: examples/sixty_seconds/test_place_order_flow.py -->
13
+ ```python
14
+ """The smallest test that drives a real path through its collaborators, traced: the
15
+ ``narrative_trace`` fixture is the capture, and every collaborator on the path is wrapped with it.
16
+ After a run, ``narrative-traces/structural/<module>/<test>.nt`` holds the flow's shape and
17
+ ``narrative-traces/traces/<module>/<test>.md`` its values."""
18
+
19
+ from narrativetrace import ContextVarNarrativeContext, trace_object
20
+
21
+
22
+ class Inventory:
23
+ def reserve(self, product_id: str, quantity: int) -> str:
24
+ return f"R-{product_id}-{quantity}"
25
+
26
+
27
+ class OrderService:
28
+ def __init__(self, inventory: Inventory) -> None:
29
+ self._inventory = inventory
30
+
31
+ def place_order(self, customer_id: str, product_id: str, quantity: int) -> str:
32
+ reservation = self._inventory.reserve(product_id, quantity)
33
+ return f"ORD-{customer_id}-{reservation}"
34
+
35
+
36
+ def test_customer_places_an_order(narrative_trace: ContextVarNarrativeContext) -> None:
37
+ inventory = trace_object(Inventory(), narrative_trace)
38
+ service = trace_object(OrderService(inventory), narrative_trace)
39
+
40
+ assert service.place_order("cust-1", "prod-42", 3) == "ORD-cust-1-R-prod-42-3"
41
+ ```
42
+ <!-- /snippet -->
43
+
44
+ **done when:** the test ran with the reported input, and its .md narrative was written by this run
45
+
46
+ **verify:** `uv run pytest <the test that reproduces the symptom>`
47
+
48
+ **failure:** no .md for the test appears under narrative-traces/traces — the test does not request the narrative_trace fixture, the collaborators on the path are not wrapped with it, or the pytest plugin is not registered. Fix: run narrativetrace-doctor and apply its fix, then run the test again
49
+
50
+ ## 2. Find the symptom in the values
51
+
52
+ ```bash
53
+ uv run python -c 'import pathlib; print(*sorted(pathlib.Path("narrative-traces/traces").rglob("*.md")), sep="\n")'
54
+ ```
55
+
56
+ **done when:** the reported value is in this run's .md narrative, or the reproducing test fails on it — a symptom that does not reproduce is said so, and the loop stops here
57
+
58
+ ## 3. Across threads, read the sequence diagram first
59
+
60
+ **when:** only when the path crosses threads or tasks — the .nt shows a fork, async or fire-and-forget marker — or the symptom is about order (a call that ran before or after another); otherwise go straight to localizing
61
+
62
+ ```bash
63
+ uv run python -c 'import pathlib; print(*sorted(pathlib.Path("narrative-traces/diagrams").rglob("*.mmd")), sep="\n")'
64
+ ```
65
+
66
+ **done when:** the reproduction's .mmd was read before any span's values, and the reply says which call ran before which across the threads, by the span id in each call's note — the span it points to is the one localized next
67
+
68
+ ## 4. Localize by reading: name the first span where a value diverges
69
+
70
+ **done when:** the reproduction's .md spans were read from the root down until the first one whose inputs are what the symptom implies but whose result, the value it passes on, or the branch it takes is not; the reply names that span by its id (#1.3) and the boundary — the collaborator, the parameter or return, the value that arrived and the value that left — before any code is changed: by reading, not by stepping through a debugger or adding prints
71
+
72
+ ## 5. Bisect by span, not by file
73
+
74
+ **when:** only when the value went into the diverging span right and came out wrong, and what happens in between is more than that span's own few lines; otherwise the diverging span is the defect — go on to the fix
75
+
76
+ ```bash
77
+ uv run pytest <the test that reproduces the symptom>
78
+ ```
79
+
80
+ **done when:** only the sub-tree under the diverging id was read on each re-run — the spans whose id begins with it (#1.3, #1.3.1, #1.3.2) — and where the work inside that span is not traced, the collaborator it calls was wrapped with trace_object in the reproducing test, as the listing above wraps its inventory, and the run repeated, until the divergence sits in the smallest span that has it; never not_traced_field, __nt_not_traced__ or @not_traced to narrow — in Python they redact a value, they do not scope a trace
81
+
82
+ ## 6. Hand a defect in NarrativeTrace itself to narrativetrace-feedback
83
+
84
+ **when:** only when the trace and the code disagree — a call the code makes has no span, a span shows a value the code did not pass, one span has two ids in two flavours — or the diverging span is inside NarrativeTrace; otherwise go on to the fix
85
+
86
+ **done when:** narrativetrace-feedback was started with the span id and the value-free .nt — never a value from the trace — and the project's code was not changed to work around it; the loop ends with that hand-off
87
+
88
+ ## 7. Fix it in the diverging span, re-run the same input, read the same span
89
+
90
+ ```bash
91
+ uv run pytest <the test that reproduces the symptom>
92
+ ```
93
+
94
+ **done when:** the change is in the code of that span — the method the diverging id names, or what it calls — and the reproducing test passes; the same span, by the same id, now carries the value the symptom implied; a change anywhere else that makes the test pass silences the symptom and leaves the defect, so it is undone
95
+
96
+ **verify:** `uv run pytest <the test that reproduces the symptom>`
97
+
98
+ ## 8. Check that nothing else moved
99
+
100
+ ```bash
101
+ uv run python -c 'import pathlib; print(*sorted(pathlib.Path("narrative-traces/structural").rglob("*.nt")), sep="\n")'
102
+ ```
103
+
104
+ **done when:** the fixed run's .nt was read whole and compared, line by line, with the call lines of the reproduction's .md — a red run writes no .nt (the .nt on disk is the last green one), so the shape before the fix is the .md's calls and ids without their values: the same calls in the same order under the same ids; a value fix moves no line of a value-free trace, and every line that did move is named by its id in the reply and either explained by the fix or undone
105
+
106
+ ## 9. Keep the reproduction as the regression test
107
+
108
+ **done when:** the reproducing test stays in the suite with the input from the symptom and asserts the value the fixed span now carries — not only that nothing raises — so it fails when the fix is undone; its structural trace is what the pin below makes the baseline
109
+
110
+ ## 10. Turn approval mode on
111
+
112
+ **when:** if approval mode is off — nothing sets NARRATIVETRACE_APPROVAL or `approval = true`, or the doctor's config.approval-mode finding fails; when it is already on, go straight to the run
113
+
114
+ **done when:** `pyproject.toml` carries `approval = true` under `[tool.narrativetrace]`, and `.gitignore` carries the line `*.received.nt` so a review copy is never committed
115
+
116
+ **verify:** `uv run python -c 'import sys; from narrativetrace.config import ConfigResolver; value = (ConfigResolver().resolve("approval", "") or "").strip().lower(); sys.exit(0 if value in {"1", "true", "yes", "on"} else 1)'`
117
+
118
+ **failure:** the verify still exits 1 after the edit — the key sits under another table, the project also has a narrativetrace.toml (two configuration sources stop every run with DuplicateConfigurationError), or NARRATIVETRACE_APPROVAL=false is set in the environment, which wins over both. Fix: keep one source — `approval = true` directly under `[tool.narrativetrace]`, or at the top of narrativetrace.toml — unset the variable, and run the verify again
119
+
120
+ ## 11. Run the suite in approval mode and show every .received.nt
121
+
122
+ ```bash
123
+ uv run pytest || true
124
+ uv run python -c 'import pathlib; print(*sorted(pathlib.Path("test-narratives").rglob("*.received.nt")), sep="\n")'
125
+ ```
126
+
127
+ **done when:** approval mode compares every traced test, not only the one this skill ran, so the run that writes the review copies is the whole suite; the first run of a test with no baseline fails on purpose — that failure is what writes its review copy — and the whole text of each .received.nt that run wrote is in the reply, not a summary of it — it holds names and shape and no value, which is why it is safe to commit once approved; where a .approved.nt already existed, the reply also names what the delta changed, by span id, in the program's own words, and whether it was meant
128
+
129
+ ## 12. Ask once whether to pin it, then stop the turn
130
+
131
+ **done when:** the answer is the user's next message, never something assumed in this one. Do not run narrativetrace-approve before the user says yes — promoting is the pinning. Everything else — the report, every caveat, and what promoting does — goes before the question; the question is ONE sentence ending in a question mark and it is the reply's last line, so a reply whose last line is a sentence after the question has not asked it.
132
+
133
+ ## 13. Promote what was shown, and nothing else
134
+
135
+ ```bash
136
+ uv run narrativetrace-approve
137
+ ```
138
+
139
+ **done when:** each .approved.nt now holds exactly the text that was shown and no .received.nt is left beside it — `git status --short test-narratives` lists the new or changed .approved.nt files and nothing else; those are what get committed — and the whole suite passes again after the promotion
140
+
141
+ **verify:** `uv run pytest`
142
+
143
+ ## 14. Report the root cause as the trace showed it
144
+
145
+ **done when:** the root cause in the user's own terms — the span id where it diverged, the value that arrived and the value that left, the branch it took — and what the fix changed in that span; every claim cites the span id it rests on, from the .nt or .md read in this session: a claim without an id is not a claim. The report is written in full before the pin question, where the gate puts it, and the closing reply after the promotion names the span id again in its one-line summary of the cause
146
+
147
+ ## Which flavour answers which question
148
+
149
+ | flavour | where | carries | answers |
150
+ |---|---|---|---|
151
+ | structural `.nt` | `narrative-traces/structural/<test module>/<test>.nt` | shape only: calls, order, nesting, parameter names, multiplicity, span ids — dozens of lines for a whole flow | did the flow do what I meant? |
152
+ | Markdown narrative | `narrative-traces/traces/<test module>/<test>.md` | values (redacted), outcomes, durations | what value crossed this boundary? — read one span, by id |
153
+ | indented text | `IndentedTextRenderer`, the failing test's output | the same values as plain text | the same question, in a console or a failure message |
154
+ | sequence diagram | `narrative-traces/diagrams/<test module>/<test>.mmd` | who called whom, in order, across threads and tasks | ordering across components, threads and tasks |
155
+ | approval delta | the failing test's message: `.received.nt` against `.approved.nt` under `test-narratives/` | what changed in the shape, citing both sides' ids | is this change intended? |
156
+ | prose | `ProseRenderer` | narration for a person | explaining the flow to the user — never read it to check the code |
157
+
158
+ Use the cheapest flavour that answers the question, and look at values only where the shape says
159
+ to look. A span id (`#1`, `#1.3`, `#1.3.2`) is the span's position in the tree and the same in
160
+ every flavour: find in the `.md` the span the `.nt` flagged, by its id. Redaction stays on — the
161
+ deny-list and `[REDACTED]` are never turned off to see more; a redacted value that matters is
162
+ reasoned about by its parameter name and the shape around it.
163
+
164
+ ## Shapes that mean something went wrong
165
+
166
+ - a call made twice that the intent makes once
167
+ - a call before its precondition — a notification before the payment that it announces
168
+ - a branch never taken that the intent takes
169
+ - a retry that masks a failure
170
+ - a side effect inside a loop
171
+ - a swallowed exception: a thrown outcome `!!` under a call that returned normally
172
+ - a cleanup that never ran
173
+ - a value crossing a boundary that should have been redacted
174
+
175
+ ## Always
176
+
177
+ - Cite a span id for every claim about the trace. (an id points at one span in every flavour, so a reviewer can check the claim; a claim without one cannot be checked)
178
+ - Narrow to one span before reading its values. (debugging is where values pay for themselves, but only on the span that diverged — a whole trace of values buries the one that matters)
179
+ - Show the whole .received.nt before asking anything. (the baseline becomes the contract every later change is held to, and a person can only approve what they have actually read)
180
+ - End the turn on the question, with nothing after it. (the answer is the user's next message, never something assumed in this one. Do not run narrativetrace-approve before the user says yes — promoting is the pinning.)
181
+
182
+ ## Never
183
+
184
+ - Never change code before the diverging span is named. (a fix made before the trace says where the value went wrong is a guess, and a guess that turns the test green hides the defect it missed)
185
+ - Never make the symptom go away somewhere other than the diverging span. (a correction downstream, a caught exception or a changed expectation silences the symptom and leaves the defect for the next caller of that span)
186
+ - Never turn redaction off to see more. (a redacted value that matters is reasoned about by its name and shape; turning redaction off puts the user's secrets in the transcript)
187
+ - Never promote a baseline in the turn that asked. (approval is the user's next message -- a yes assumed in the same turn is not one)
188
+ - Never edit the .received.nt after showing it. (what was approved has to be what is promoted, so a changed run is rendered again and shown again)
189
+ - Never commit a .received.nt. (it is the review copy; the committed contract is the .approved.nt)