narrativetrace 0.2.0__tar.gz → 0.4.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 (253) hide show
  1. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/.gitignore +5 -0
  2. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/LICENSE +2 -2
  3. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/PKG-INFO +1 -1
  4. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/pyproject.toml +1 -1
  5. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/__init__.py +1 -1
  6. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/_skills/agents/add-narrative-tracing/SKILL.md +31 -7
  7. narrativetrace-0.4.0/src/narrativetrace/_skills/agents/add-narrativetrace-clarity/SKILL.md +106 -0
  8. narrativetrace-0.4.0/src/narrativetrace/_skills/agents/narrativetrace-debug/SKILL.md +189 -0
  9. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/_skills/agents/narrativetrace-doctor/SKILL.md +3 -2
  10. narrativetrace-0.4.0/src/narrativetrace/_skills/agents/narrativetrace-explain/SKILL.md +100 -0
  11. narrativetrace-0.4.0/src/narrativetrace/_skills/agents/narrativetrace-feedback/SKILL.md +71 -0
  12. narrativetrace-0.4.0/src/narrativetrace/_skills/agents/narrativetrace-verify/SKILL.md +165 -0
  13. narrativetrace-0.4.0/src/narrativetrace/_skills/catalogue.json +47 -0
  14. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/_skills/claude/add-narrative-tracing/SKILL.md +32 -8
  15. narrativetrace-0.4.0/src/narrativetrace/_skills/claude/add-narrativetrace-clarity/SKILL.md +108 -0
  16. narrativetrace-0.4.0/src/narrativetrace/_skills/claude/narrativetrace-debug/SKILL.md +190 -0
  17. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/_skills/claude/narrativetrace-doctor/SKILL.md +4 -3
  18. narrativetrace-0.4.0/src/narrativetrace/_skills/claude/narrativetrace-explain/SKILL.md +101 -0
  19. narrativetrace-0.4.0/src/narrativetrace/_skills/claude/narrativetrace-feedback/SKILL.md +72 -0
  20. narrativetrace-0.4.0/src/narrativetrace/_skills/claude/narrativetrace-verify/SKILL.md +166 -0
  21. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/doctor/cli_bin.py +44 -3
  22. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/doctor/environment.py +26 -8
  23. narrativetrace-0.4.0/src/narrativetrace/doctor/feedback_cli.py +350 -0
  24. narrativetrace-0.4.0/src/narrativetrace/doctor/gh_auth_probe.py +105 -0
  25. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/escape.py +12 -3
  26. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/approval.py +4 -4
  27. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/line_diff.py +54 -17
  28. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/paths.py +17 -8
  29. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/structural_delta.py +39 -15
  30. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/concurrency.py +16 -1
  31. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/indented.py +65 -46
  32. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/markdown.py +68 -40
  33. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/prose.py +45 -21
  34. narrativetrace-0.4.0/src/narrativetrace/render/span_id.py +139 -0
  35. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/structural.py +50 -23
  36. narrativetrace-0.4.0/tests/doctor/test_adversarial_feedback_cli.py +149 -0
  37. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/doctor/test_cli_built_wheels.py +17 -0
  38. narrativetrace-0.4.0/tests/doctor/test_cli_feedback_arguments.py +162 -0
  39. narrativetrace-0.4.0/tests/doctor/test_cli_feedback_verb.py +505 -0
  40. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/doctor/test_doctor_cli_bin.py +26 -1
  41. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/doctor/test_doctor_environment.py +43 -0
  42. narrativetrace-0.4.0/tests/doctor/test_gh_auth_probe.py +115 -0
  43. narrativetrace-0.4.0/tests/fixtures/java-negative-expense.nt +8 -0
  44. narrativetrace-0.4.0/tests/logging_wiring.py +40 -0
  45. narrativetrace-0.4.0/tests/test_adversarial_skills_render.py +123 -0
  46. narrativetrace-0.4.0/tests/test_adversarial_vendor_validation.py +244 -0
  47. narrativetrace-0.4.0/tests/test_adversarial_verify_all_heavy.py +134 -0
  48. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_approval.py +27 -0
  49. narrativetrace-0.4.0/tests/test_context7_check.py +241 -0
  50. narrativetrace-0.4.0/tests/test_errors_only_level.py +89 -0
  51. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_escape.py +15 -0
  52. narrativetrace-0.4.0/tests/test_feedback_docs.py +95 -0
  53. narrativetrace-0.4.0/tests/test_framework_table_docs.py +131 -0
  54. narrativetrace-0.4.0/tests/test_installer_from_another_package.py +359 -0
  55. narrativetrace-0.4.0/tests/test_issue_form_publishes.py +194 -0
  56. narrativetrace-0.4.0/tests/test_logging_wiring.py +28 -0
  57. narrativetrace-0.4.0/tests/test_notice_render_targets.py +51 -0
  58. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_output.py +17 -0
  59. narrativetrace-0.4.0/tests/test_parameter_value_redaction.py +70 -0
  60. narrativetrace-0.4.0/tests/test_platform_pointer_from_another_package.py +312 -0
  61. narrativetrace-0.4.0/tests/test_platform_table_docs.py +119 -0
  62. narrativetrace-0.4.0/tests/test_promotion_render.py +43 -0
  63. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_publish_public.py +105 -0
  64. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_publish_workflow_packages.py +5 -2
  65. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_render.py +13 -12
  66. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_skills_carrier_wheel_contents.py +24 -4
  67. narrativetrace-0.4.0/tests/test_skills_render.py +213 -0
  68. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_smoke.py +1 -1
  69. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_snippet_check.py +54 -1
  70. narrativetrace-0.4.0/tests/test_span_id.py +120 -0
  71. narrativetrace-0.4.0/tests/test_span_id_flavours.py +197 -0
  72. narrativetrace-0.4.0/tests/test_span_id_props.py +167 -0
  73. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_structural.py +125 -13
  74. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_structural_conformance.py +55 -0
  75. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_structural_delta.py +106 -0
  76. narrativetrace-0.4.0/tests/test_vendor_validation.py +274 -0
  77. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all.py +2 -2
  78. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all_heavy.py +38 -0
  79. narrativetrace-0.4.0/tests/test_version_guard_ignores_a_stale_installed_carrier.py +65 -0
  80. narrativetrace-0.2.0/src/narrativetrace/_skills/catalogue.json +0 -17
  81. narrativetrace-0.2.0/tests/fixtures/java-negative-expense.nt +0 -8
  82. narrativetrace-0.2.0/tests/test_installer_from_another_package.py +0 -181
  83. narrativetrace-0.2.0/tests/test_skills_render.py +0 -120
  84. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/README.md +0 -0
  85. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/_boundary.py +0 -0
  86. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/canonical.py +0 -0
  87. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/chapter.py +0 -0
  88. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/cli.py +0 -0
  89. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/concurrency.py +0 -0
  90. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/config.py +0 -0
  91. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/context.py +0 -0
  92. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/context_export.py +0 -0
  93. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/decorators.py +0 -0
  94. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/doctor/__init__.py +0 -0
  95. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/events.py +0 -0
  96. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/export.py +0 -0
  97. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/groups.py +0 -0
  98. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/identity.py +0 -0
  99. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/ids.py +0 -0
  100. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/levels.py +0 -0
  101. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/logging_bridge.py +0 -0
  102. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/loss.py +0 -0
  103. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/markers.py +0 -0
  104. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/metadata.py +0 -0
  105. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/namer.py +0 -0
  106. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/national_id_shapes.py +0 -0
  107. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/nodes.py +0 -0
  108. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/outcomes.py +0 -0
  109. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/__init__.py +0 -0
  110. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/artifact_identity.py +0 -0
  111. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/manifest.py +0 -0
  112. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/reporter.py +0 -0
  113. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/run_identity.py +0 -0
  114. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/warnings.py +0 -0
  115. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/output/writer.py +0 -0
  116. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/pipeline/__init__.py +0 -0
  117. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/pipeline/bounded_buffer.py +0 -0
  118. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/pipeline/buffered_consumer.py +0 -0
  119. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/pipeline/dual_path.py +0 -0
  120. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/pipeline/event_store.py +0 -0
  121. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/pipeline/perishable.py +0 -0
  122. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/pipeline/subscriber.py +0 -0
  123. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/py.typed +0 -0
  124. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/redacted_paths.py +0 -0
  125. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/redaction.py +0 -0
  126. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/__init__.py +0 -0
  127. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/base.py +0 -0
  128. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/camel.py +0 -0
  129. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/frontmatter.py +0 -0
  130. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/scenario.py +0 -0
  131. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/scenario_result.py +0 -0
  132. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/value_delta.py +0 -0
  133. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/render/value_reference.py +0 -0
  134. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/rendering.py +0 -0
  135. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/secret_value_shapes.py +0 -0
  136. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/signature.py +0 -0
  137. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/span.py +0 -0
  138. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/template.py +0 -0
  139. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/trace_object.py +0 -0
  140. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/tree.py +0 -0
  141. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/tree_canonical.py +0 -0
  142. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/tree_walk.py +0 -0
  143. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/src/narrativetrace/values.py +0 -0
  144. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/conftest.py +0 -0
  145. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/doctor/test_cli_installer_arguments.py +0 -0
  146. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/doctor/test_cli_installer_verbs.py +0 -0
  147. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/live_annotation_support.py +0 -0
  148. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/stress_support.py +0 -0
  149. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_artifact_identity.py +0 -0
  150. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_async_concurrency_kind.py +0 -0
  151. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_async_lifecycle_contract.py +0 -0
  152. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_async_snapshot_adoption.py +0 -0
  153. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_bench_capture.py +0 -0
  154. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_bench_redaction.py +0 -0
  155. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_bench_rendering.py +0 -0
  156. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_canonical.py +0 -0
  157. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_canonical_props.py +0 -0
  158. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_capture_identity.py +0 -0
  159. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_chapter.py +0 -0
  160. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_chapter_props.py +0 -0
  161. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_check_no_license_headers.py +0 -0
  162. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_cli.py +0 -0
  163. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_comment_hygiene.py +0 -0
  164. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_concurrency_info.py +0 -0
  165. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_config.py +0 -0
  166. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_console_delta.py +0 -0
  167. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_context.py +0 -0
  168. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_context_export.py +0 -0
  169. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_context_extra.py +0 -0
  170. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_context_stress.py +0 -0
  171. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_contract_check.py +0 -0
  172. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_contract_dispatch_coverage.py +0 -0
  173. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_contract_lint.py +0 -0
  174. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_coverage_source_completeness.py +0 -0
  175. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_decorators.py +0 -0
  176. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_deep_fixture_budget.py +0 -0
  177. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_distribution_licensing.py +0 -0
  178. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_duplication_check.py +0 -0
  179. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_duplication_report.py +0 -0
  180. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_events.py +0 -0
  181. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_export.py +0 -0
  182. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_fuzz_report.py +0 -0
  183. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_groups.py +0 -0
  184. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_identity.py +0 -0
  185. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_identity_conformance.py +0 -0
  186. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_ids.py +0 -0
  187. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_levels.py +0 -0
  188. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_live_snapshot_scope_visibility.py +0 -0
  189. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_logging_bridge.py +0 -0
  190. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_manifest.py +0 -0
  191. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_metadata.py +0 -0
  192. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_mutation_accounting.py +0 -0
  193. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_mutation_baseline_selection.py +0 -0
  194. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_mutation_gate.py +0 -0
  195. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_mutation_tooling.py +0 -0
  196. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_mutation_workers.py +0 -0
  197. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_namer.py +0 -0
  198. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_national_id_shapes.py +0 -0
  199. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_nested_chain_adoption.py +0 -0
  200. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_no_poison_contract.py +0 -0
  201. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_nodes.py +0 -0
  202. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_outcomes.py +0 -0
  203. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_parameter_name_redaction.py +0 -0
  204. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_pipeline_buffered.py +0 -0
  205. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_pipeline_dual_path.py +0 -0
  206. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_pipeline_perishable.py +0 -0
  207. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_pipeline_props.py +0 -0
  208. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_pipeline_stress.py +0 -0
  209. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_pipeline_subscriber.py +0 -0
  210. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_publish_agents_md_section.py +0 -0
  211. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_publish_public_host_venv.py +0 -0
  212. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_quality_gate_status.py +0 -0
  213. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_redacted_paths.py +0 -0
  214. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_redacted_value_containment.py +0 -0
  215. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_redaction.py +0 -0
  216. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_render_reads_state.py +0 -0
  217. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_render_reentrancy.py +0 -0
  218. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_rendering.py +0 -0
  219. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_rendering_extra.py +0 -0
  220. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_run_identity.py +0 -0
  221. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_run_security_tool.py +0 -0
  222. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_scenario_result.py +0 -0
  223. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_secret_value_shapes.py +0 -0
  224. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_signature.py +0 -0
  225. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_span.py +0 -0
  226. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_structlog_dependency.py +0 -0
  227. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_template.py +0 -0
  228. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_timeout_floor.py +0 -0
  229. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_trace_loss.py +0 -0
  230. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_trace_object.py +0 -0
  231. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_trace_stack_live_child.py +0 -0
  232. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_translation_check.py +0 -0
  233. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_tree.py +0 -0
  234. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_tree_canonical.py +0 -0
  235. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_tree_props.py +0 -0
  236. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_tree_walk.py +0 -0
  237. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_tree_writes_guard.py +0 -0
  238. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_value_delta.py +0 -0
  239. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_value_reference.py +0 -0
  240. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_values.py +0 -0
  241. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all_arch_stress.py +0 -0
  242. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all_exec.py +0 -0
  243. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all_schema.py +0 -0
  244. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all_security.py +0 -0
  245. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all_static.py +0 -0
  246. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_all_testrun.py +0 -0
  247. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_publication_packages.py +0 -0
  248. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_publication_provenance.py +0 -0
  249. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_verify_publication_registry.py +0 -0
  250. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_version_literals.py +0 -0
  251. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_version_literals_props.py +0 -0
  252. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/test_writer_structural.py +0 -0
  253. {narrativetrace-0.2.0 → narrativetrace-0.4.0}/tests/wheel_support.py +0 -0
@@ -28,3 +28,8 @@ build/
28
28
  # narrativetrace-pytest's default per-test artifact directory -- on by default, opt out with
29
29
  # NARRATIVETRACE_OUTPUT=false (see documentation/what-to-commit.md)
30
30
  narrative-traces/
31
+
32
+ # The recipe scenario's approved baselines are source; the files an approval run writes beside them
33
+ # are not (documentation/what-to-commit.md)
34
+ examples/recipes/checkout/test-narratives/**/*.received.nt
35
+ examples/recipes/checkout/test-narratives/**/*.incomplete.nt
@@ -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.2.0. The Licensed Work is (c) 2026 Empower Agile.
10
+ Licensed Work: NarrativeTrace for Python (the narrativetrace PyPI distributions) version 0.4.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-10-05 for this version)
21
+ Change Date: Four years from the date the Licensed Work is published. (2030-10-16 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.2.0
3
+ Version: 0.4.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "narrativetrace"
3
- version = "0.2.0"
3
+ version = "0.4.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"
@@ -97,7 +97,7 @@ from narrativetrace.values import (
97
97
  StringVal,
98
98
  )
99
99
 
100
- __version__ = "0.2.0"
100
+ __version__ = "0.4.0"
101
101
 
102
102
  __all__ = [
103
103
  "NOOP_CONTEXT",
@@ -1,6 +1,6 @@
1
1
  ---
2
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. 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."
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
4
  ---
5
5
 
6
6
  # add-narrative-tracing
@@ -21,7 +21,27 @@ sys.exit(1 if bad else 0)
21
21
 
22
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
23
 
24
- ## 2. First trace: wrap, call, render, run
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
25
45
 
26
46
  <!-- snippet: examples/sixty_seconds/main.py -->
27
47
  ```python
@@ -56,7 +76,9 @@ print(IndentedTextRenderer().render(context.capture_trace()))
56
76
 
57
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
58
78
 
59
- ## 3. Send it to your logger
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
60
82
 
61
83
  <!-- snippet: examples/sixty_seconds/main_with_logger.py -->
62
84
  ```python
@@ -110,7 +132,7 @@ export_to_logger(trace) # new: sends the same captured trace through the config
110
132
 
111
133
  **verify:** `uv run python main_with_logger.py`
112
134
 
113
- ## 4. Run the doctor and resolve its findings
135
+ ## 5. Run the doctor and resolve its findings
114
136
 
115
137
  ```bash
116
138
  uv run narrativetrace doctor || true
@@ -119,15 +141,17 @@ uv run narrativetrace doctor || true
119
141
  **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
120
142
  report = json.load(sys.stdin)
121
143
  findings = report.get("findings")
122
- sys.exit(1 if not isinstance(findings, list) or len(findings) != 12 else 0)
144
+ sys.exit(1 if not isinstance(findings, list) or len(findings) != 19 else 0)
123
145
  '`
124
146
 
125
- ## 5. Install the skills for next time
147
+ ## 6. Install the skills for next time
126
148
 
127
149
  ```bash
128
150
  uv run narrativetrace init --dry-run
129
151
  ```
130
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
+
131
155
  **verify:** `uv run python -c 'import pathlib, subprocess, sys
132
156
  result = subprocess.run(
133
157
  ["uv", "run", "narrativetrace", "init", "--dry-run"], capture_output=True, text=True
@@ -150,5 +174,5 @@ sys.exit(0 if result.returncode == 0 and shown and untouched else 1)
150
174
  ## Never
151
175
 
152
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)
153
- - Never skip the final narrativetrace doctor call. (it is the seam that catches anything these four steps did not -- narrativetrace-doctor owns diagnosis from here)
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)
154
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)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: narrativetrace-doctor
3
- description: "Diagnoses a NarrativeTrace Python install and configuration. Use when nothing is being traced, no trace output files appear, DuplicateConfigurationError shows up on startup, a *args method's parameters render as one args: [...] value, or you are not sure NarrativeTrace is wired up correctly. Checks the interpreter and pytest versions, that all eight narrativetrace-* packages agree on one version, NARRATIVETRACE_OUTPUT, that the pytest plugin is registered and not disabled, whether the NarrativeTrace agent skills are installed and current, unrecognized narrativetrace.toml keys, an imported-but-unused not_traced_field/__nt_not_traced__ marker, whether redaction is proven in a test, and stale approval-trace diffs. Read-only -- makes no changes. Say 'check my narrativetrace setup', 'is narrativetrace broken', or 'why isn't anything being traced' to invoke it."
3
+ description: "Diagnoses a NarrativeTrace Python install and configuration. Use when nothing is being traced, no trace output files appear, DuplicateConfigurationError shows up on startup, a *args method's parameters render as one args: [...] value, or you are not sure NarrativeTrace is wired up correctly. Checks the interpreter and pytest versions, that all eight narrativetrace-* packages agree on one version, NARRATIVETRACE_OUTPUT, that the pytest plugin is registered and not disabled, whether the NarrativeTrace agent skills are installed and current, unrecognized narrativetrace.toml keys, an imported-but-unused not_traced_field/__nt_not_traced__ marker, whether redaction is proven in a test, stale approval-trace diffs, and approved baselines nothing compares because approval mode is off. Read-only -- makes no changes. Say 'check my narrativetrace setup', 'is narrativetrace broken', or 'why isn't anything being traced' to invoke it."
4
4
  ---
5
5
 
6
6
  # narrativetrace-doctor
@@ -14,7 +14,7 @@ uv run narrativetrace doctor || true
14
14
  **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
15
15
  report = json.load(sys.stdin)
16
16
  findings = report.get("findings")
17
- sys.exit(1 if not isinstance(findings, list) or len(findings) != 12 else 0)
17
+ sys.exit(1 if not isinstance(findings, list) or len(findings) != 19 else 0)
18
18
  '`
19
19
 
20
20
  **failure:** the CLI's JSON output does not parse, or is missing findings — the CLI crashed instead of reporting a finding. Fix: re-run `uv run narrativetrace doctor --json` directly and read the raw output -- a crash here is a doctor bug, never a project finding
@@ -81,6 +81,7 @@ else:
81
81
  ## Always
82
82
 
83
83
  - Run the doctor CLI and read its report before making any change. (the tested tooling already computed the finding -- re-deriving it by hand risks disagreeing with what ships)
84
+ - If a check is wrong, or its fix does not work, report it with the narrativetrace-feedback skill. (a wrong finding costs every project that hits it until somebody says so, and that skill shows you the whole report and files nothing without your answer)
84
85
 
85
86
  ## Never
86
87
 
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: narrativetrace-explain
3
+ description: "Use when someone asks what an application or a flow actually does, with nothing broken and no change needing verification: 'show me what my application actually does', 'walk me through what happens when a user checks out', 'I inherited this service, how does an order flow', 'what does this endpoint call'. Load it BEFORE reading source files to describe them: it explains a Python application by running one scenario with NarrativeTrace on and reading the trace of what happened \u2014 evidence of the run, not a reading of the code. Decides first whether a trace is worth it: a question ten lines of source answer is answered from the source, with no install and no run. Otherwise it runs one existing test or entry point for that flow \u2014 installing through add-narrative-tracing when the project has none \u2014 reads the value-free structural trace, and explains the run by span id, marking what it inferred and naming the code no span covered. Hands a defect to narrativetrace-debug, a change to narrativetrace-verify."
4
+ ---
5
+
6
+ # narrativetrace-explain
7
+
8
+ ## 1. Decide whether the source already answers it, and say so
9
+
10
+ **done when:** before anything is installed or run, the reply says which it is: 'reading the source: <the reason>' when the question is about a pure function, one class with no collaborator, or a path ten lines of source settle — the answer is then that reading, with no install and no run, and the skill ends there; or 'tracing: <the reason>' when the flow crosses two or more collaborators over a boundary, branches, retries, runs async or concurrently, carries state between calls, or is code nobody in this session wrote. A whole flow's .nt is dozens of lines: cheap where the path is not obvious, waste where the source is already the answer
11
+
12
+ ## 2. Choose the one scenario that already exercises the flow
13
+
14
+ **done when:** the reply names ONE scenario — an existing test, or the entry point a reader would run — and which call of the flow asked about it drives; a scenario is written only where the project has none, and the reply says which of the two it is. Two scenarios is two traces to read where one answers the question
15
+
16
+ ## 3. Install tracing first when the project has none
17
+
18
+ **when:** only when the project has no NarrativeTrace at all — no dependency on it in pyproject.toml and nothing under narrative-traces; where it is already installed, go straight to the run
19
+
20
+ **done when:** add-narrative-tracing was followed to its own first trace and this skill resumed at the run below; nothing beyond that install was changed — an explanation does not refactor the code it is about
21
+
22
+ ## 4. Run that one scenario with tracing on
23
+
24
+ **when:** the project already has a test or entry point that exercises the flow — run that one, as it is; where none exists, add-narrative-tracing's own first-trace step is how one gets written, and it comes back here
25
+
26
+ ```bash
27
+ uv run pytest <the test that exercises the flow you were asked about>
28
+ ```
29
+
30
+ **done when:** the scenario ran and wrote a trace of its own: narrative-traces/structural holds an .nt for it, and narrative-traces/traces the .md beside it — a run that writes nothing is the failure below, never an answer; and the scenario ran as it was found, because a scenario edited to make the flow clearer is no longer the flow
31
+
32
+ **failure:** the scenario runs but nothing appears under narrative-traces — the test does not request the narrative_trace fixture, the collaborators on the flow are not wrapped with it, or the pytest plugin is not registered, so there is nothing to read. Fix: run narrativetrace-doctor and apply its fix, then run the scenario again
33
+
34
+ ## 5. Read the structural trace first, in the order it ran
35
+
36
+ ```bash
37
+ uv run python -c 'import pathlib; print(*sorted(pathlib.Path("narrative-traces/structural").rglob("*.nt")), sep="\n")'
38
+ ```
39
+
40
+ **done when:** the .nt the scenario just wrote was opened and read whole before any value was looked at, and the reply walks it in its own order by span id (#1, #1.1, #1.2): which call ran under which, how many times, and which branch the run took — the flavours below say which file answers which question
41
+
42
+ ## 6. Open values only where the question needs one
43
+
44
+ **when:** only when the question is about a value that crossed a boundary, or about where the time went — the .nt carries neither; otherwise the shape is the whole answer
45
+
46
+ ```bash
47
+ uv run python -c 'import pathlib; print(*sorted(pathlib.Path("narrative-traces/traces").rglob("*.md")), sep="\n")'
48
+ ```
49
+
50
+ **done when:** only the spans the question needs were read in the .md narrative, found by the id the .nt gave them — not the whole file; a [REDACTED] value stays redacted and is reasoned about by its parameter name and the shape around it
51
+
52
+ ## 7. Hand off when it stops being an explanation
53
+
54
+ **when:** only when the run shows something wrong — one of the shapes below, or a value that is not what the flow implies — or a change of this session's own is what the user is really asking about; otherwise go on to the explanation
55
+
56
+ **done when:** narrativetrace-debug was started, with the span id of the first call that looks wrong, when the run showed a defect; or narrativetrace-verify when what needs reading is a change this session made. The explanation written so far goes with the hand-off, and this skill fixes nothing itself
57
+
58
+ ## 8. Explain the run by span id, and mark what you inferred
59
+
60
+ **done when:** the explanation is in the user's own words and in the trace's own order: the calls the flow made, each claim citing the span id it rests on — a claim without an id is not a claim, and only ids in the .nt or .md read in this session count — the branch it took, and where the time went when that was the question. Every sentence that goes beyond what the trace shows is marked as an inference, and the last part names what was NOT observed: the code no span covers, because only wrapped or intercepted code has one
61
+
62
+ ## Which flavour answers which question
63
+
64
+ | flavour | where | carries | answers |
65
+ |---|---|---|---|
66
+ | 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? |
67
+ | Markdown narrative | `narrative-traces/traces/<test module>/<test>.md` | values (redacted), outcomes, durations | what value crossed this boundary? — read one span, by id |
68
+ | indented text | `IndentedTextRenderer`, the failing test's output | the same values as plain text | the same question, in a console or a failure message |
69
+ | sequence diagram | `narrative-traces/diagrams/<test module>/<test>.mmd` | who called whom, in order, across threads and tasks | ordering across components, threads and tasks |
70
+ | 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? |
71
+ | prose | `ProseRenderer` | narration for a person | explaining the flow to the user — never read it to check the code |
72
+
73
+ Use the cheapest flavour that answers the question, and look at values only where the shape says
74
+ to look. A span id (`#1`, `#1.3`, `#1.3.2`) is the span's position in the tree and the same in
75
+ every flavour: find in the `.md` the span the `.nt` flagged, by its id. Redaction stays on — the
76
+ deny-list and `[REDACTED]` are never turned off to see more; a redacted value that matters is
77
+ reasoned about by its parameter name and the shape around it.
78
+
79
+ ## Shapes that mean something went wrong
80
+
81
+ - a call made twice that the intent makes once
82
+ - a call before its precondition — a notification before the payment that it announces
83
+ - a branch never taken that the intent takes
84
+ - a retry that masks a failure
85
+ - a side effect inside a loop
86
+ - a swallowed exception: a thrown outcome `!!` under a call that returned normally
87
+ - a cleanup that never ran
88
+ - a value crossing a boundary that should have been redacted
89
+
90
+ ## Always
91
+
92
+ - Answer from the source when ten lines of source answer the question (an install and a run cost the user time and tokens, and a trace of a pure function shows one call; the trace earns its keep where the path is not obvious — two collaborators over a boundary, a branch, a retry, state carried between calls)
93
+ - 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)
94
+ - Separate what the trace showed from what you inferred. (a reader cannot check a claim that mixes the two, and the inference is exactly where an explanation is wrong while sounding most certain)
95
+
96
+ ## Never
97
+
98
+ - Never present an unobserved call as something the trace showed. (only wrapped or intercepted code has a span, so a call no span covers is an inference; naming it as observed is how an explanation becomes confidently wrong about the part nobody measured)
99
+ - Never change the code in order to explain it. (the user asked what the code does, not for a change — wrapping one more collaborator inside a scenario is reading, editing what runs in production is not)
100
+ - 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)