narrativetrace 0.1.2__tar.gz → 0.2.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 (251) hide show
  1. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/.gitignore +1 -0
  2. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/LICENSE +2 -2
  3. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/PKG-INFO +4 -2
  4. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/README.md +2 -1
  5. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/pyproject.toml +14 -3
  6. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/__init__.py +1 -1
  7. narrativetrace-0.2.0/src/narrativetrace/_skills/agents/add-narrative-tracing/SKILL.md +154 -0
  8. narrativetrace-0.2.0/src/narrativetrace/_skills/agents/narrativetrace-doctor/SKILL.md +88 -0
  9. narrativetrace-0.2.0/src/narrativetrace/_skills/catalogue.json +17 -0
  10. narrativetrace-0.2.0/src/narrativetrace/_skills/claude/add-narrative-tracing/SKILL.md +156 -0
  11. narrativetrace-0.2.0/src/narrativetrace/_skills/claude/narrativetrace-doctor/SKILL.md +90 -0
  12. narrativetrace-0.2.0/src/narrativetrace/doctor/__init__.py +27 -0
  13. narrativetrace-0.2.0/src/narrativetrace/doctor/cli_bin.py +442 -0
  14. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/doctor/environment.py +43 -6
  15. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/escape.py +2 -2
  16. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/logging_bridge.py +2 -2
  17. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/national_id_shapes.py +1 -1
  18. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/approval.py +5 -5
  19. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/writer.py +16 -22
  20. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/pipeline/__init__.py +2 -3
  21. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/redacted_paths.py +15 -18
  22. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/value_delta.py +1 -1
  23. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/rendering.py +387 -85
  24. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/secret_value_shapes.py +2 -1
  25. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/signature.py +2 -2
  26. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/span.py +2 -2
  27. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/template.py +67 -33
  28. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/trace_object.py +10 -3
  29. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/tree_walk.py +2 -2
  30. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/conftest.py +21 -1
  31. narrativetrace-0.2.0/tests/doctor/test_cli_built_wheels.py +223 -0
  32. narrativetrace-0.2.0/tests/doctor/test_cli_installer_arguments.py +226 -0
  33. narrativetrace-0.2.0/tests/doctor/test_cli_installer_verbs.py +796 -0
  34. narrativetrace-0.2.0/tests/doctor/test_doctor_cli_bin.py +181 -0
  35. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/doctor/test_doctor_environment.py +78 -13
  36. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_chapter.py +6 -0
  37. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_check_no_license_headers.py +48 -23
  38. narrativetrace-0.2.0/tests/test_comment_hygiene.py +175 -0
  39. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_context_extra.py +8 -0
  40. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_contract_check.py +2 -3
  41. narrativetrace-0.2.0/tests/test_contract_dispatch_coverage.py +70 -0
  42. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_contract_lint.py +14 -65
  43. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_coverage_source_completeness.py +3 -2
  44. narrativetrace-0.2.0/tests/test_deep_fixture_budget.py +251 -0
  45. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_distribution_licensing.py +51 -7
  46. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_export.py +8 -0
  47. narrativetrace-0.2.0/tests/test_installer_from_another_package.py +181 -0
  48. narrativetrace-0.2.0/tests/test_mutation_baseline_selection.py +57 -0
  49. narrativetrace-0.2.0/tests/test_mutation_workers.py +135 -0
  50. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_no_poison_contract.py +28 -6
  51. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_output.py +8 -0
  52. narrativetrace-0.2.0/tests/test_publish_public_host_venv.py +134 -0
  53. narrativetrace-0.2.0/tests/test_publish_workflow_packages.py +47 -0
  54. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_redacted_paths.py +22 -7
  55. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_render.py +16 -0
  56. narrativetrace-0.2.0/tests/test_render_reads_state.py +544 -0
  57. narrativetrace-0.2.0/tests/test_render_reentrancy.py +264 -0
  58. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_rendering.py +92 -32
  59. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_rendering_extra.py +23 -5
  60. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_run_security_tool.py +4 -4
  61. narrativetrace-0.2.0/tests/test_skills_carrier_wheel_contents.py +104 -0
  62. narrativetrace-0.2.0/tests/test_skills_render.py +120 -0
  63. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_smoke.py +1 -1
  64. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_snippet_check.py +92 -7
  65. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_template.py +71 -33
  66. narrativetrace-0.2.0/tests/test_timeout_floor.py +167 -0
  67. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_trace_object.py +30 -4
  68. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_tree.py +15 -0
  69. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_tree_canonical.py +6 -0
  70. narrativetrace-0.2.0/tests/test_tree_writes_guard.py +407 -0
  71. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_value_reference.py +8 -0
  72. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all_heavy.py +4 -4
  73. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all_security.py +85 -1
  74. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_publication_registry.py +57 -0
  75. narrativetrace-0.2.0/tests/test_version_literals.py +419 -0
  76. narrativetrace-0.2.0/tests/test_version_literals_props.py +70 -0
  77. narrativetrace-0.2.0/tests/wheel_support.py +86 -0
  78. narrativetrace-0.1.2/src/narrativetrace/doctor/__init__.py +0 -22
  79. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/approval_traces.py +0 -37
  80. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/llms_before_you_start.py +0 -46
  81. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/not_traced_unused.py +0 -64
  82. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/output_env.py +0 -52
  83. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/package_versions.py +0 -51
  84. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/parameter_names.py +0 -46
  85. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/pytest_plugin_registered.py +0 -82
  86. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/pytest_version.py +0 -50
  87. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/python_version.py +0 -38
  88. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/redaction_proof.py +0 -40
  89. narrativetrace-0.1.2/src/narrativetrace/doctor/checks/unknown_config_keys.py +0 -51
  90. narrativetrace-0.1.2/src/narrativetrace/doctor/cli_bin.py +0 -115
  91. narrativetrace-0.1.2/src/narrativetrace/doctor/doc_urls.py +0 -31
  92. narrativetrace-0.1.2/src/narrativetrace/doctor/doctor.py +0 -43
  93. narrativetrace-0.1.2/src/narrativetrace/doctor/finding.py +0 -21
  94. narrativetrace-0.1.2/src/narrativetrace/doctor/render.py +0 -48
  95. narrativetrace-0.1.2/src/narrativetrace/doctor/types.py +0 -89
  96. narrativetrace-0.1.2/src/narrativetrace/doctor/version_lite.py +0 -91
  97. narrativetrace-0.1.2/tests/doctor/conftest.py +0 -58
  98. narrativetrace-0.1.2/tests/doctor/snapshot_factory_types.py +0 -18
  99. narrativetrace-0.1.2/tests/doctor/test_check_approval_traces.py +0 -48
  100. narrativetrace-0.1.2/tests/doctor/test_check_llms_before_you_start.py +0 -36
  101. narrativetrace-0.1.2/tests/doctor/test_check_not_traced_unused.py +0 -60
  102. narrativetrace-0.1.2/tests/doctor/test_check_output_env.py +0 -48
  103. narrativetrace-0.1.2/tests/doctor/test_check_package_versions.py +0 -59
  104. narrativetrace-0.1.2/tests/doctor/test_check_parameter_names.py +0 -29
  105. narrativetrace-0.1.2/tests/doctor/test_check_pytest_plugin_registered.py +0 -80
  106. narrativetrace-0.1.2/tests/doctor/test_check_pytest_version.py +0 -59
  107. narrativetrace-0.1.2/tests/doctor/test_check_python_version.py +0 -47
  108. narrativetrace-0.1.2/tests/doctor/test_check_redaction_proof.py +0 -46
  109. narrativetrace-0.1.2/tests/doctor/test_check_unknown_config_keys.py +0 -31
  110. narrativetrace-0.1.2/tests/doctor/test_doctor_cli_bin.py +0 -130
  111. narrativetrace-0.1.2/tests/doctor/test_doctor_finding.py +0 -33
  112. narrativetrace-0.1.2/tests/doctor/test_doctor_registry.py +0 -51
  113. narrativetrace-0.1.2/tests/doctor/test_doctor_render.py +0 -53
  114. narrativetrace-0.1.2/tests/doctor/test_doctor_version_lite.py +0 -81
  115. narrativetrace-0.1.2/tests/test_llms_banner.py +0 -325
  116. narrativetrace-0.1.2/tests/test_skills_render.py +0 -45
  117. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/_boundary.py +0 -0
  118. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/canonical.py +0 -0
  119. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/chapter.py +0 -0
  120. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/cli.py +0 -0
  121. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/concurrency.py +0 -0
  122. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/config.py +0 -0
  123. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/context.py +0 -0
  124. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/context_export.py +0 -0
  125. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/decorators.py +0 -0
  126. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/events.py +0 -0
  127. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/export.py +0 -0
  128. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/groups.py +0 -0
  129. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/identity.py +0 -0
  130. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/ids.py +0 -0
  131. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/levels.py +0 -0
  132. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/loss.py +0 -0
  133. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/markers.py +0 -0
  134. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/metadata.py +0 -0
  135. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/namer.py +0 -0
  136. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/nodes.py +0 -0
  137. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/outcomes.py +0 -0
  138. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/__init__.py +0 -0
  139. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/artifact_identity.py +0 -0
  140. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/line_diff.py +0 -0
  141. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/manifest.py +0 -0
  142. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/paths.py +0 -0
  143. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/reporter.py +0 -0
  144. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/run_identity.py +0 -0
  145. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/structural_delta.py +0 -0
  146. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/output/warnings.py +0 -0
  147. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/pipeline/bounded_buffer.py +0 -0
  148. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/pipeline/buffered_consumer.py +0 -0
  149. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/pipeline/dual_path.py +0 -0
  150. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/pipeline/event_store.py +0 -0
  151. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/pipeline/perishable.py +0 -0
  152. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/pipeline/subscriber.py +0 -0
  153. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/py.typed +0 -0
  154. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/redaction.py +0 -0
  155. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/__init__.py +0 -0
  156. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/base.py +0 -0
  157. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/camel.py +0 -0
  158. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/concurrency.py +0 -0
  159. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/frontmatter.py +0 -0
  160. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/indented.py +0 -0
  161. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/markdown.py +0 -0
  162. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/prose.py +0 -0
  163. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/scenario.py +0 -0
  164. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/scenario_result.py +0 -0
  165. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/structural.py +0 -0
  166. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/render/value_reference.py +0 -0
  167. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/tree.py +0 -0
  168. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/tree_canonical.py +0 -0
  169. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/src/narrativetrace/values.py +0 -0
  170. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/fixtures/java-negative-expense.nt +0 -0
  171. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/live_annotation_support.py +0 -0
  172. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/stress_support.py +0 -0
  173. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_approval.py +0 -0
  174. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_artifact_identity.py +0 -0
  175. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_async_concurrency_kind.py +0 -0
  176. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_async_lifecycle_contract.py +0 -0
  177. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_async_snapshot_adoption.py +0 -0
  178. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_bench_capture.py +0 -0
  179. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_bench_redaction.py +0 -0
  180. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_bench_rendering.py +0 -0
  181. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_canonical.py +0 -0
  182. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_canonical_props.py +0 -0
  183. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_capture_identity.py +0 -0
  184. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_chapter_props.py +0 -0
  185. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_cli.py +0 -0
  186. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_concurrency_info.py +0 -0
  187. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_config.py +0 -0
  188. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_console_delta.py +0 -0
  189. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_context.py +0 -0
  190. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_context_export.py +0 -0
  191. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_context_stress.py +0 -0
  192. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_decorators.py +0 -0
  193. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_duplication_check.py +0 -0
  194. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_duplication_report.py +0 -0
  195. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_escape.py +0 -0
  196. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_events.py +0 -0
  197. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_fuzz_report.py +0 -0
  198. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_groups.py +0 -0
  199. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_identity.py +0 -0
  200. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_identity_conformance.py +0 -0
  201. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_ids.py +0 -0
  202. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_levels.py +0 -0
  203. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_live_snapshot_scope_visibility.py +0 -0
  204. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_logging_bridge.py +0 -0
  205. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_manifest.py +0 -0
  206. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_metadata.py +0 -0
  207. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_mutation_accounting.py +0 -0
  208. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_mutation_gate.py +0 -0
  209. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_mutation_tooling.py +0 -0
  210. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_namer.py +0 -0
  211. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_national_id_shapes.py +0 -0
  212. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_nested_chain_adoption.py +0 -0
  213. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_nodes.py +0 -0
  214. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_outcomes.py +0 -0
  215. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_parameter_name_redaction.py +0 -0
  216. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_pipeline_buffered.py +0 -0
  217. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_pipeline_dual_path.py +0 -0
  218. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_pipeline_perishable.py +0 -0
  219. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_pipeline_props.py +0 -0
  220. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_pipeline_stress.py +0 -0
  221. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_pipeline_subscriber.py +0 -0
  222. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_publish_agents_md_section.py +0 -0
  223. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_publish_public.py +0 -0
  224. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_quality_gate_status.py +0 -0
  225. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_redacted_value_containment.py +0 -0
  226. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_redaction.py +0 -0
  227. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_run_identity.py +0 -0
  228. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_scenario_result.py +0 -0
  229. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_secret_value_shapes.py +0 -0
  230. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_signature.py +0 -0
  231. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_span.py +0 -0
  232. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_structlog_dependency.py +0 -0
  233. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_structural.py +0 -0
  234. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_structural_conformance.py +0 -0
  235. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_structural_delta.py +0 -0
  236. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_trace_loss.py +0 -0
  237. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_trace_stack_live_child.py +0 -0
  238. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_translation_check.py +0 -0
  239. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_tree_props.py +0 -0
  240. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_tree_walk.py +0 -0
  241. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_value_delta.py +0 -0
  242. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_values.py +0 -0
  243. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all.py +0 -0
  244. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all_arch_stress.py +0 -0
  245. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all_exec.py +0 -0
  246. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all_schema.py +0 -0
  247. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all_static.py +0 -0
  248. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_all_testrun.py +0 -0
  249. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_publication_packages.py +0 -0
  250. {narrativetrace-0.1.2 → narrativetrace-0.2.0}/tests/test_verify_publication_provenance.py +0 -0
  251. {narrativetrace-0.1.2 → narrativetrace-0.2.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.2.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-05 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.2.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.2.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.2.0"
101
101
 
102
102
  __all__ = [
103
103
  "NOOP_CONTEXT",
@@ -0,0 +1,154 @@
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. 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. First trace: wrap, call, render, run
25
+
26
+ <!-- snippet: examples/sixty_seconds/main.py -->
27
+ ```python
28
+ # main.py
29
+ from narrativetrace import ContextVarNarrativeContext, IndentedTextRenderer, TraceId, trace_object
30
+
31
+
32
+ class OrderService:
33
+ def place_order(self, customer_id, product_id, quantity):
34
+ return f"ORD-{customer_id}-{product_id}-{quantity}"
35
+
36
+
37
+ # snippet:begin fixedTraceId
38
+ # A fixed trace id, adopted so this page's embedded output always names the same trace. A real
39
+ # run generates a random one every time (never this -- it is this DEMO's own constant, not the
40
+ # library default) via the same TraceId.adopt_trace_id a servlet-style boundary uses for an
41
+ # inbound trace header.
42
+ DEMO_TRACE_ID = TraceId("a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4")
43
+
44
+ # snippet:end fixedTraceId
45
+
46
+ context = ContextVarNarrativeContext()
47
+ context.adopt_trace_id(DEMO_TRACE_ID)
48
+ service = trace_object(OrderService(), context)
49
+ service.place_order("cust-1", "prod-42", 3)
50
+
51
+ print(IndentedTextRenderer().render(context.capture_trace()))
52
+ ```
53
+ <!-- /snippet -->
54
+
55
+ **verify:** `uv run python main.py`
56
+
57
+ **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
+
59
+ ## 3. Send it to your logger
60
+
61
+ <!-- snippet: examples/sixty_seconds/main_with_logger.py -->
62
+ ```python
63
+ # main.py
64
+ import logging # new: stdlib logging -- the sink this step sends the trace to
65
+ import sys # new: stdout target for the handler below
66
+
67
+ from narrativetrace import (
68
+ ContextVarNarrativeContext,
69
+ IndentedTextRenderer,
70
+ NarrativeContextFilter, # new: injects traceName/runName onto every log record
71
+ TraceId,
72
+ export_to_logger, # new: replays an already-captured trace through your logger, one call
73
+ trace_object,
74
+ )
75
+
76
+
77
+ class OrderService:
78
+ def place_order(self, customer_id, product_id, quantity):
79
+ return f"ORD-{customer_id}-{product_id}-{quantity}"
80
+
81
+
82
+ # snippet:begin fixedTraceId
83
+ # A fixed trace id, adopted so this page's embedded output always names the same trace. A real
84
+ # run generates a random one every time (never this -- it is this DEMO's own constant, not the
85
+ # library default) via the same TraceId.adopt_trace_id a servlet-style boundary uses for an
86
+ # inbound trace header.
87
+ DEMO_TRACE_ID = TraceId("a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4")
88
+
89
+ # snippet:end fixedTraceId
90
+
91
+ # new: a plain stdlib logging setup -- the shape a real app's own logging config already has
92
+ handler = logging.StreamHandler(sys.stdout)
93
+ handler.addFilter(NarrativeContextFilter()) # new: makes traceName/runName available below
94
+ # new: DEBUG so export_to_logger's records pass the handler; the format reads the filter's keys
95
+ logging.basicConfig(
96
+ level=logging.DEBUG, format="[%(traceName)s] [%(runName)s] %(message)s", handlers=[handler]
97
+ )
98
+
99
+ context = ContextVarNarrativeContext()
100
+ context.adopt_trace_id(DEMO_TRACE_ID)
101
+ service = trace_object(OrderService(), context)
102
+ service.place_order("cust-1", "prod-42", 3)
103
+
104
+ trace = context.capture_trace() # new: capture once, reuse for both the print and the export
105
+ print(IndentedTextRenderer().render(trace))
106
+
107
+ export_to_logger(trace) # new: sends the same captured trace through the configured logger
108
+ ```
109
+ <!-- /snippet -->
110
+
111
+ **verify:** `uv run python main_with_logger.py`
112
+
113
+ ## 4. Run the doctor and resolve its findings
114
+
115
+ ```bash
116
+ uv run narrativetrace doctor || true
117
+ ```
118
+
119
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
120
+ report = json.load(sys.stdin)
121
+ findings = report.get("findings")
122
+ sys.exit(1 if not isinstance(findings, list) or len(findings) != 12 else 0)
123
+ '`
124
+
125
+ ## 5. Install the skills for next time
126
+
127
+ ```bash
128
+ uv run narrativetrace init --dry-run
129
+ ```
130
+
131
+ **verify:** `uv run python -c 'import pathlib, subprocess, sys
132
+ result = subprocess.run(
133
+ ["uv", "run", "narrativetrace", "init", "--dry-run"], capture_output=True, text=True
134
+ )
135
+ shown = (
136
+ "action(s)" in result.stdout
137
+ and ".agents/skills" in result.stdout
138
+ and "AGENTS.md" in result.stdout
139
+ )
140
+ untouched = not pathlib.Path(".agents/skills").exists() and not pathlib.Path("AGENTS.md").exists()
141
+ sys.exit(0 if result.returncode == 0 and shown and untouched else 1)
142
+ '`
143
+
144
+ **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
145
+
146
+ ## Always
147
+
148
+ - 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)
149
+
150
+ ## Never
151
+
152
+ - 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)
154
+ - 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,88 @@
1
+ ---
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."
4
+ ---
5
+
6
+ # narrativetrace-doctor
7
+
8
+ ## 1. Run the doctor and read its report
9
+
10
+ ```bash
11
+ uv run narrativetrace doctor || true
12
+ ```
13
+
14
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
15
+ report = json.load(sys.stdin)
16
+ findings = report.get("findings")
17
+ sys.exit(1 if not isinstance(findings, list) or len(findings) != 12 else 0)
18
+ '`
19
+
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
21
+
22
+ ## 2. Prove redaction in a test
23
+
24
+ ```bash
25
+ uv run python -c 'print(
26
+ "Render a call with a deny-listed parameter name (e.g. password or token) in a test and "
27
+ "assert the output contains [REDACTED], and that a neighboring non-sensitive value is still "
28
+ "present."
29
+ )
30
+ '
31
+ ```
32
+
33
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
34
+ report = json.load(sys.stdin)
35
+ by_id = {f["id"]: f for f in report["findings"]}
36
+ finding = by_id.get("trap.redaction-proof")
37
+ sys.exit(1 if finding is None or finding["status"] not in ("pass", "fail") else 0)
38
+ '`
39
+
40
+ **failure:** not_traced_field/__nt_not_traced__ is present but never applied — trusting redaction by inspection instead of proving it in a test. Fix: render a call with a deny-listed parameter name and assert the output contains "[REDACTED]"
41
+
42
+ ## 3. Read the rendered trace before asserting
43
+
44
+ ```bash
45
+ uv run python -c 'import pathlib
46
+ root = pathlib.Path("narrative-traces")
47
+ files = sorted(root.rglob("*.md"), key=lambda p: p.stat().st_mtime) if root.is_dir() else []
48
+ if not files:
49
+ print("no rendered .md file found yet under narrative-traces -- run your tests or app once")
50
+ else:
51
+ newest = files[-1]
52
+ print(newest)
53
+ print(newest.read_text(encoding="utf-8"))
54
+ '
55
+ ```
56
+
57
+ ## 4. Approval flow: diff the structural trace, not just values
58
+
59
+ **Flagged:** unstudied -- eval cell pending
60
+
61
+ ```bash
62
+ uv run python -c 'import pathlib
63
+ root = pathlib.Path("test-narratives")
64
+ received = sorted(root.rglob("*.received.nt")) if root.is_dir() else []
65
+ if not received:
66
+ print("no approval traces configured yet under test-narratives -- nothing pending")
67
+ else:
68
+ newest = received[-1]
69
+ approved = newest.with_name(newest.name.replace(".received.nt", ".approved.nt"))
70
+ print("received: " + str(newest))
71
+ if approved.is_file():
72
+ print("approved: " + str(approved))
73
+ print(approved.read_text(encoding="utf-8"))
74
+ print("--- vs received ---")
75
+ else:
76
+ print("no .approved.nt yet -- first approval, review then promote")
77
+ print(newest.read_text(encoding="utf-8"))
78
+ '
79
+ ```
80
+
81
+ ## Always
82
+
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
+
85
+ ## Never
86
+
87
+ - Never have this skill edit, generate, or delete a file. (narrativetrace-doctor is scoped read-only by design -- generation of the redaction-proof test itself is a separate, later skill)
88
+ - Never claim a finding passed without having run the doctor CLI in this session. (self-reported success overstates reality -- a build claimed green that does not reproduce from clean is not evidence; verify is never 'ask the agent')
@@ -0,0 +1,17 @@
1
+ {
2
+ "runtime": "python",
3
+ "skills": [
4
+ {
5
+ "name": "narrativetrace-doctor",
6
+ "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.",
7
+ "agents": "agents/narrativetrace-doctor/SKILL.md",
8
+ "claude": "claude/narrativetrace-doctor/SKILL.md"
9
+ },
10
+ {
11
+ "name": "add-narrative-tracing",
12
+ "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.",
13
+ "agents": "agents/add-narrative-tracing/SKILL.md",
14
+ "claude": "claude/add-narrative-tracing/SKILL.md"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,156 @@
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. 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
+ when_to_use: "A project does not have NarrativeTrace yet, or has the package installed but has never produced a trace, or traces print to the console but nothing forwards them to a real logger."
5
+ allowed-tools: uv, git
6
+ ---
7
+
8
+ # add-narrative-tracing
9
+
10
+ ## 1. Install with the real toolchain
11
+
12
+ ```bash
13
+ uv sync --all-packages
14
+ ```
15
+
16
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
17
+ report = json.load(sys.stdin)
18
+ bad = [f for f in report["findings"] if f["id"].startswith("toolchain.") and f["status"] != "pass"]
19
+ if bad:
20
+ print(json.dumps(bad))
21
+ sys.exit(1 if bad else 0)
22
+ '`
23
+
24
+ **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`
25
+
26
+ ## 2. First trace: wrap, call, render, run
27
+
28
+ <!-- snippet: examples/sixty_seconds/main.py -->
29
+ ```python
30
+ # main.py
31
+ from narrativetrace import ContextVarNarrativeContext, IndentedTextRenderer, TraceId, trace_object
32
+
33
+
34
+ class OrderService:
35
+ def place_order(self, customer_id, product_id, quantity):
36
+ return f"ORD-{customer_id}-{product_id}-{quantity}"
37
+
38
+
39
+ # snippet:begin fixedTraceId
40
+ # A fixed trace id, adopted so this page's embedded output always names the same trace. A real
41
+ # run generates a random one every time (never this -- it is this DEMO's own constant, not the
42
+ # library default) via the same TraceId.adopt_trace_id a servlet-style boundary uses for an
43
+ # inbound trace header.
44
+ DEMO_TRACE_ID = TraceId("a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4")
45
+
46
+ # snippet:end fixedTraceId
47
+
48
+ context = ContextVarNarrativeContext()
49
+ context.adopt_trace_id(DEMO_TRACE_ID)
50
+ service = trace_object(OrderService(), context)
51
+ service.place_order("cust-1", "prod-42", 3)
52
+
53
+ print(IndentedTextRenderer().render(context.capture_trace()))
54
+ ```
55
+ <!-- /snippet -->
56
+
57
+ **verify:** `uv run python main.py`
58
+
59
+ **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
60
+
61
+ ## 3. Send it to your logger
62
+
63
+ <!-- snippet: examples/sixty_seconds/main_with_logger.py -->
64
+ ```python
65
+ # main.py
66
+ import logging # new: stdlib logging -- the sink this step sends the trace to
67
+ import sys # new: stdout target for the handler below
68
+
69
+ from narrativetrace import (
70
+ ContextVarNarrativeContext,
71
+ IndentedTextRenderer,
72
+ NarrativeContextFilter, # new: injects traceName/runName onto every log record
73
+ TraceId,
74
+ export_to_logger, # new: replays an already-captured trace through your logger, one call
75
+ trace_object,
76
+ )
77
+
78
+
79
+ class OrderService:
80
+ def place_order(self, customer_id, product_id, quantity):
81
+ return f"ORD-{customer_id}-{product_id}-{quantity}"
82
+
83
+
84
+ # snippet:begin fixedTraceId
85
+ # A fixed trace id, adopted so this page's embedded output always names the same trace. A real
86
+ # run generates a random one every time (never this -- it is this DEMO's own constant, not the
87
+ # library default) via the same TraceId.adopt_trace_id a servlet-style boundary uses for an
88
+ # inbound trace header.
89
+ DEMO_TRACE_ID = TraceId("a1b2c3d4a1b2c3d4a1b2c3d4a1b2c3d4")
90
+
91
+ # snippet:end fixedTraceId
92
+
93
+ # new: a plain stdlib logging setup -- the shape a real app's own logging config already has
94
+ handler = logging.StreamHandler(sys.stdout)
95
+ handler.addFilter(NarrativeContextFilter()) # new: makes traceName/runName available below
96
+ # new: DEBUG so export_to_logger's records pass the handler; the format reads the filter's keys
97
+ logging.basicConfig(
98
+ level=logging.DEBUG, format="[%(traceName)s] [%(runName)s] %(message)s", handlers=[handler]
99
+ )
100
+
101
+ context = ContextVarNarrativeContext()
102
+ context.adopt_trace_id(DEMO_TRACE_ID)
103
+ service = trace_object(OrderService(), context)
104
+ service.place_order("cust-1", "prod-42", 3)
105
+
106
+ trace = context.capture_trace() # new: capture once, reuse for both the print and the export
107
+ print(IndentedTextRenderer().render(trace))
108
+
109
+ export_to_logger(trace) # new: sends the same captured trace through the configured logger
110
+ ```
111
+ <!-- /snippet -->
112
+
113
+ **verify:** `uv run python main_with_logger.py`
114
+
115
+ ## 4. Run the doctor and resolve its findings
116
+
117
+ ```bash
118
+ uv run narrativetrace doctor || true
119
+ ```
120
+
121
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
122
+ report = json.load(sys.stdin)
123
+ findings = report.get("findings")
124
+ sys.exit(1 if not isinstance(findings, list) or len(findings) != 12 else 0)
125
+ '`
126
+
127
+ ## 5. Install the skills for next time
128
+
129
+ ```bash
130
+ uv run narrativetrace init --dry-run
131
+ ```
132
+
133
+ **verify:** `uv run python -c 'import pathlib, subprocess, sys
134
+ result = subprocess.run(
135
+ ["uv", "run", "narrativetrace", "init", "--dry-run"], capture_output=True, text=True
136
+ )
137
+ shown = (
138
+ "action(s)" in result.stdout
139
+ and ".agents/skills" in result.stdout
140
+ and "AGENTS.md" in result.stdout
141
+ )
142
+ untouched = not pathlib.Path(".agents/skills").exists() and not pathlib.Path("AGENTS.md").exists()
143
+ sys.exit(0 if result.returncode == 0 and shown and untouched else 1)
144
+ '`
145
+
146
+ **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
147
+
148
+ ## Always
149
+
150
+ - 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)
151
+
152
+ ## Never
153
+
154
+ - 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)
155
+ - 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)
156
+ - 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,90 @@
1
+ ---
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."
4
+ when_to_use: "A project already has NarrativeTrace installed and something about it is not working, or an agent wants a pre-flight check before wiring it into new code."
5
+ allowed-tools: uv, git
6
+ ---
7
+
8
+ # narrativetrace-doctor
9
+
10
+ ## 1. Run the doctor and read its report
11
+
12
+ ```bash
13
+ uv run narrativetrace doctor || true
14
+ ```
15
+
16
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
17
+ report = json.load(sys.stdin)
18
+ findings = report.get("findings")
19
+ sys.exit(1 if not isinstance(findings, list) or len(findings) != 12 else 0)
20
+ '`
21
+
22
+ **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
23
+
24
+ ## 2. Prove redaction in a test
25
+
26
+ ```bash
27
+ uv run python -c 'print(
28
+ "Render a call with a deny-listed parameter name (e.g. password or token) in a test and "
29
+ "assert the output contains [REDACTED], and that a neighboring non-sensitive value is still "
30
+ "present."
31
+ )
32
+ '
33
+ ```
34
+
35
+ **verify:** `uv run narrativetrace doctor --json | uv run python -c 'import json, sys
36
+ report = json.load(sys.stdin)
37
+ by_id = {f["id"]: f for f in report["findings"]}
38
+ finding = by_id.get("trap.redaction-proof")
39
+ sys.exit(1 if finding is None or finding["status"] not in ("pass", "fail") else 0)
40
+ '`
41
+
42
+ **failure:** not_traced_field/__nt_not_traced__ is present but never applied — trusting redaction by inspection instead of proving it in a test. Fix: render a call with a deny-listed parameter name and assert the output contains "[REDACTED]"
43
+
44
+ ## 3. Read the rendered trace before asserting
45
+
46
+ ```bash
47
+ uv run python -c 'import pathlib
48
+ root = pathlib.Path("narrative-traces")
49
+ files = sorted(root.rglob("*.md"), key=lambda p: p.stat().st_mtime) if root.is_dir() else []
50
+ if not files:
51
+ print("no rendered .md file found yet under narrative-traces -- run your tests or app once")
52
+ else:
53
+ newest = files[-1]
54
+ print(newest)
55
+ print(newest.read_text(encoding="utf-8"))
56
+ '
57
+ ```
58
+
59
+ ## 4. Approval flow: diff the structural trace, not just values
60
+
61
+ **Flagged:** unstudied -- eval cell pending
62
+
63
+ ```bash
64
+ uv run python -c 'import pathlib
65
+ root = pathlib.Path("test-narratives")
66
+ received = sorted(root.rglob("*.received.nt")) if root.is_dir() else []
67
+ if not received:
68
+ print("no approval traces configured yet under test-narratives -- nothing pending")
69
+ else:
70
+ newest = received[-1]
71
+ approved = newest.with_name(newest.name.replace(".received.nt", ".approved.nt"))
72
+ print("received: " + str(newest))
73
+ if approved.is_file():
74
+ print("approved: " + str(approved))
75
+ print(approved.read_text(encoding="utf-8"))
76
+ print("--- vs received ---")
77
+ else:
78
+ print("no .approved.nt yet -- first approval, review then promote")
79
+ print(newest.read_text(encoding="utf-8"))
80
+ '
81
+ ```
82
+
83
+ ## Always
84
+
85
+ - 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)
86
+
87
+ ## Never
88
+
89
+ - Never have this skill edit, generate, or delete a file. (narrativetrace-doctor is scoped read-only by design -- generation of the redaction-proof test itself is a separate, later skill)
90
+ - Never claim a finding passed without having run the doctor CLI in this session. (self-reported success overstates reality -- a build claimed green that does not reproduce from clean is not evidence; verify is never 'ask the agent')
@@ -0,0 +1,27 @@
1
+ # SPDX-License-Identifier: BUSL-1.1
2
+ # Licensed under the Business Source License 1.1 (see LICENSE); Change Date: four
3
+ # years from publication; Change License: Apache-2.0
4
+ # Copyright (c) 2026 Empower Agile
5
+ """``narrativetrace doctor``'s one impure half, and the console script every verb goes through.
6
+
7
+ Every check, the report, both renderings and the whole agent-skills installer live in
8
+ :mod:`narrativetrace_tooling` — the zero-dependency library this distribution's console script
9
+ launches, and which a second entry point can embed without dragging the runtime in with it. What
10
+ stays here is what cannot move:
11
+
12
+ * :mod:`narrativetrace.doctor.environment` — builds the
13
+ :class:`~narrativetrace_tooling.doctor.types.DoctorSnapshot` by walking the real project and the
14
+ installed-distribution metadata. It resolves configuration through
15
+ :class:`narrativetrace.config.ConfigResolver`, the same resolver the runtime itself uses, so the
16
+ doctor reads a project exactly as the runtime does rather than guessing at the precedence again.
17
+ * :mod:`narrativetrace.doctor.cli_bin` — the ``narrativetrace`` console script: verb dispatch for
18
+ ``doctor``, ``init`` and ``uninstall``, process exit codes, stdout and stderr. It sits beside the
19
+ doctor's reader rather than higher up because ``doctor`` was its first verb and this module path
20
+ is the one ``[project.scripts]`` names; the launcher itself is about all three.
21
+
22
+ Not part of the package's public API (:mod:`narrativetrace`'s ``__all__``) — reached only through
23
+ the ``narrativetrace`` console script, the same way ``narrativetrace-approve``
24
+ (:mod:`narrativetrace.cli`) is a console-script-only module.
25
+ """
26
+
27
+ from __future__ import annotations