narrativetrace 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/LICENSE +2 -2
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/PKG-INFO +1 -1
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/pyproject.toml +1 -1
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/__init__.py +1 -1
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/_skills/agents/add-narrative-tracing/SKILL.md +31 -7
- narrativetrace-0.3.0/src/narrativetrace/_skills/agents/add-narrativetrace-clarity/SKILL.md +106 -0
- narrativetrace-0.3.0/src/narrativetrace/_skills/agents/narrativetrace-debug/SKILL.md +189 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/_skills/agents/narrativetrace-doctor/SKILL.md +3 -2
- narrativetrace-0.3.0/src/narrativetrace/_skills/agents/narrativetrace-feedback/SKILL.md +71 -0
- narrativetrace-0.3.0/src/narrativetrace/_skills/agents/narrativetrace-verify/SKILL.md +165 -0
- narrativetrace-0.3.0/src/narrativetrace/_skills/catalogue.json +41 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/_skills/claude/add-narrative-tracing/SKILL.md +32 -8
- narrativetrace-0.3.0/src/narrativetrace/_skills/claude/add-narrativetrace-clarity/SKILL.md +108 -0
- narrativetrace-0.3.0/src/narrativetrace/_skills/claude/narrativetrace-debug/SKILL.md +190 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/_skills/claude/narrativetrace-doctor/SKILL.md +4 -3
- narrativetrace-0.3.0/src/narrativetrace/_skills/claude/narrativetrace-feedback/SKILL.md +72 -0
- narrativetrace-0.3.0/src/narrativetrace/_skills/claude/narrativetrace-verify/SKILL.md +166 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/doctor/cli_bin.py +40 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/doctor/environment.py +4 -1
- narrativetrace-0.3.0/src/narrativetrace/doctor/feedback_cli.py +350 -0
- narrativetrace-0.3.0/src/narrativetrace/doctor/gh_auth_probe.py +105 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/escape.py +12 -3
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/approval.py +4 -4
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/line_diff.py +54 -17
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/structural_delta.py +39 -15
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/concurrency.py +16 -1
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/indented.py +65 -46
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/markdown.py +68 -40
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/prose.py +45 -21
- narrativetrace-0.3.0/src/narrativetrace/render/span_id.py +139 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/structural.py +50 -23
- narrativetrace-0.3.0/tests/doctor/test_adversarial_feedback_cli.py +149 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/doctor/test_cli_built_wheels.py +17 -0
- narrativetrace-0.3.0/tests/doctor/test_cli_feedback_arguments.py +162 -0
- narrativetrace-0.3.0/tests/doctor/test_cli_feedback_verb.py +505 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/doctor/test_doctor_cli_bin.py +1 -1
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/doctor/test_doctor_environment.py +21 -0
- narrativetrace-0.3.0/tests/doctor/test_gh_auth_probe.py +115 -0
- narrativetrace-0.3.0/tests/fixtures/java-negative-expense.nt +8 -0
- narrativetrace-0.3.0/tests/logging_wiring.py +40 -0
- narrativetrace-0.3.0/tests/test_adversarial_skills_render.py +123 -0
- narrativetrace-0.3.0/tests/test_adversarial_vendor_validation.py +244 -0
- narrativetrace-0.3.0/tests/test_adversarial_verify_all_heavy.py +134 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_approval.py +27 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_escape.py +15 -0
- narrativetrace-0.3.0/tests/test_feedback_docs.py +81 -0
- narrativetrace-0.3.0/tests/test_framework_table_docs.py +131 -0
- narrativetrace-0.3.0/tests/test_installer_from_another_package.py +331 -0
- narrativetrace-0.3.0/tests/test_issue_form_publishes.py +194 -0
- narrativetrace-0.3.0/tests/test_logging_wiring.py +28 -0
- narrativetrace-0.3.0/tests/test_notice_render_targets.py +39 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_publish_public.py +105 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_publish_workflow_packages.py +5 -2
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_render.py +13 -12
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_skills_carrier_wheel_contents.py +21 -4
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_skills_render.py +42 -15
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_smoke.py +1 -1
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_snippet_check.py +15 -0
- narrativetrace-0.3.0/tests/test_span_id.py +120 -0
- narrativetrace-0.3.0/tests/test_span_id_flavours.py +197 -0
- narrativetrace-0.3.0/tests/test_span_id_props.py +167 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_structural.py +125 -13
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_structural_conformance.py +54 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_structural_delta.py +106 -0
- narrativetrace-0.3.0/tests/test_vendor_validation.py +274 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all.py +2 -2
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all_heavy.py +38 -0
- narrativetrace-0.2.0/src/narrativetrace/_skills/catalogue.json +0 -17
- narrativetrace-0.2.0/tests/fixtures/java-negative-expense.nt +0 -8
- narrativetrace-0.2.0/tests/test_installer_from_another_package.py +0 -181
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/.gitignore +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/README.md +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/_boundary.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/canonical.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/chapter.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/cli.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/concurrency.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/config.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/context.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/context_export.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/decorators.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/doctor/__init__.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/events.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/export.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/groups.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/identity.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/ids.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/levels.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/logging_bridge.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/loss.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/markers.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/metadata.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/namer.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/national_id_shapes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/nodes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/outcomes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/__init__.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/artifact_identity.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/manifest.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/paths.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/reporter.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/run_identity.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/warnings.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/output/writer.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/__init__.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/bounded_buffer.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/buffered_consumer.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/dual_path.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/event_store.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/perishable.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/pipeline/subscriber.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/py.typed +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/redacted_paths.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/redaction.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/__init__.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/base.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/camel.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/frontmatter.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/scenario.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/scenario_result.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/value_delta.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/render/value_reference.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/rendering.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/secret_value_shapes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/signature.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/span.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/template.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/trace_object.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/tree.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/tree_canonical.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/tree_walk.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/src/narrativetrace/values.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/conftest.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/doctor/test_cli_installer_arguments.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/doctor/test_cli_installer_verbs.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/live_annotation_support.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/stress_support.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_artifact_identity.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_async_concurrency_kind.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_async_lifecycle_contract.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_async_snapshot_adoption.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_bench_capture.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_bench_redaction.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_bench_rendering.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_canonical.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_canonical_props.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_capture_identity.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_chapter.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_chapter_props.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_check_no_license_headers.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_cli.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_comment_hygiene.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_concurrency_info.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_config.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_console_delta.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_context.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_context_export.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_context_extra.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_context_stress.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_contract_check.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_contract_dispatch_coverage.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_contract_lint.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_coverage_source_completeness.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_decorators.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_deep_fixture_budget.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_distribution_licensing.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_duplication_check.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_duplication_report.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_events.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_export.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_fuzz_report.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_groups.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_identity.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_identity_conformance.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_ids.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_levels.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_live_snapshot_scope_visibility.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_logging_bridge.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_manifest.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_metadata.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_mutation_accounting.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_mutation_baseline_selection.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_mutation_gate.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_mutation_tooling.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_mutation_workers.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_namer.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_national_id_shapes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_nested_chain_adoption.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_no_poison_contract.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_nodes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_outcomes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_output.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_parameter_name_redaction.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_pipeline_buffered.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_pipeline_dual_path.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_pipeline_perishable.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_pipeline_props.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_pipeline_stress.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_pipeline_subscriber.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_publish_agents_md_section.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_publish_public_host_venv.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_quality_gate_status.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_redacted_paths.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_redacted_value_containment.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_redaction.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_render_reads_state.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_render_reentrancy.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_rendering.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_rendering_extra.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_run_identity.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_run_security_tool.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_scenario_result.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_secret_value_shapes.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_signature.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_span.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_structlog_dependency.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_template.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_timeout_floor.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_trace_loss.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_trace_object.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_trace_stack_live_child.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_translation_check.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_tree.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_tree_canonical.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_tree_props.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_tree_walk.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_tree_writes_guard.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_value_delta.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_value_reference.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_values.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all_arch_stress.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all_exec.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all_schema.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all_security.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all_static.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_all_testrun.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_publication_packages.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_publication_provenance.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_verify_publication_registry.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_version_literals.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_version_literals_props.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/test_writer_structural.py +0 -0
- {narrativetrace-0.2.0 → narrativetrace-0.3.0}/tests/wheel_support.py +0 -0
|
@@ -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.
|
|
10
|
+
Licensed Work: NarrativeTrace for Python (the narrativetrace PyPI distributions) version 0.3.0. The Licensed Work is (c) 2026 Empower Agile.
|
|
11
11
|
|
|
12
12
|
Additional Use Grant: You may make production use of the Licensed Work for any
|
|
13
13
|
purpose, including internal use and use in products and
|
|
@@ -18,7 +18,7 @@ Additional Use Grant: You may make production use of the Licensed Work for any
|
|
|
18
18
|
parties as a logging, tracing, or code-narrative product
|
|
19
19
|
or service.
|
|
20
20
|
|
|
21
|
-
Change Date: Four years from the date the Licensed Work is published. (2030-10-
|
|
21
|
+
Change Date: Four years from the date the Licensed Work is published. (2030-10-10 for this version)
|
|
22
22
|
|
|
23
23
|
Change License: Apache License, Version 2.0
|
|
24
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: narrativetrace
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Narrative execution tracing for Python — human- and LLM-readable traces
|
|
5
5
|
Project-URL: Homepage, https://github.com/narrativetrace/narrativetrace-python
|
|
6
6
|
Project-URL: Source, https://github.com/narrativetrace/narrativetrace-python
|
|
@@ -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.
|
|
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.
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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) !=
|
|
144
|
+
sys.exit(1 if not isinstance(findings, list) or len(findings) != 19 else 0)
|
|
123
145
|
'`
|
|
124
146
|
|
|
125
|
-
##
|
|
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
|
|
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,
|
|
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) !=
|
|
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,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: narrativetrace-feedback
|
|
3
|
+
description: "Reports a defect in NarrativeTrace itself -- the library, the doctor, an agent skill, or the published install prompt. Use when a doctor finding is wrong or its fix does not work, when a skill step cannot be followed or its verify cannot be met, when the install prompt is wrong, or when the library misbehaves and the project is configured correctly. Drafts the report from this project (the install coordinates, the doctor's own JSON report, and at most one structural trace), refuses to write one that carries a value from your traces and names the rule that refused it, shows you the whole draft, and then asks once whether to file it publicly. Files nothing without your answer and sends nothing anywhere. Say 'report this to NarrativeTrace', 'the doctor's fix did not work', or 'file a bug about this skill' to invoke it."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# narrativetrace-feedback
|
|
7
|
+
|
|
8
|
+
## 1. Gather what the report needs
|
|
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) != 19 else 0)
|
|
18
|
+
'`
|
|
19
|
+
|
|
20
|
+
**failure:** the CLI is not found, or the project has no pyproject.toml — NarrativeTrace is not installed in this project's environment. Fix: report under the prompt or library category instead -- those do not need a doctor report
|
|
21
|
+
|
|
22
|
+
## 2. Draft the report and let the gate check it
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv run narrativetrace feedback draft --category <category> --step "<where it happened>" --did "<what you did>" --happened "<what happened>" --expected "<what you expected>"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**verify:** `uv run python -c 'import pathlib, sys
|
|
29
|
+
root = pathlib.Path("build/narrativetrace/feedback")
|
|
30
|
+
paths = [root / (stem + ".md") for stem in ("feedback-draft", "feedback-body")]
|
|
31
|
+
sys.exit(0 if all(p.is_file() and p.stat().st_size for p in paths) else 1)
|
|
32
|
+
'`
|
|
33
|
+
|
|
34
|
+
**failure:** the command exits 2 naming a vf.* rule — a field carries a value from this project's own run -- a rendered call line, an elapsed time, a credential-shaped string, an address. Fix: rewrite that one field to describe what happened instead of pasting it, and draft again; never work around the rule by moving the text to another field
|
|
35
|
+
|
|
36
|
+
## 3. Show the whole draft, not a summary of it
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv run python -c 'import pathlib
|
|
40
|
+
draft = pathlib.Path("build/narrativetrace/feedback") / ("feedback-draft" + ".md")
|
|
41
|
+
print(draft.read_text(encoding="utf-8"))
|
|
42
|
+
'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 4. Ask once whether to file it, then stop the turn
|
|
46
|
+
|
|
47
|
+
## 5. Print the way to file it, and nothing else
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
uv run narrativetrace feedback url --category <category> --step "<where it happened>" --did "<what you did>" --happened "<what happened>" --expected "<what you expected>"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**verify:** `uv run narrativetrace feedback url --category <category> --step "<where it happened>" --did "<what you did>" --happened "<what happened>" --expected "<what you expected>" | uv run python -c 'import sys
|
|
54
|
+
sys.exit(0 if "/issues/new?template=" in sys.stdin.read() else 1)
|
|
55
|
+
'`
|
|
56
|
+
|
|
57
|
+
## Always
|
|
58
|
+
|
|
59
|
+
- Show the whole draft before asking anything. (filing is public and permanent, and a person can only approve what they have actually read)
|
|
60
|
+
- 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 print the issue URL or run gh before the user says yes — showing the URL is the filing.)
|
|
61
|
+
- Ask in the user's own language. (the report may be written in any language, and a question nobody understands is not a question)
|
|
62
|
+
- Tell the user that filing is public, under their own account, before they answer. (a public issue shows that their project uses NarrativeTrace, and that is their decision to make knowingly)
|
|
63
|
+
- With the printed URL, name the file to paste and where. (the URL carries the short fields only; the body is never in it, so the user must paste the body file the verb names into the form's last box)
|
|
64
|
+
|
|
65
|
+
## Never
|
|
66
|
+
|
|
67
|
+
- Never attach a rendered trace, a log file or a source file. (those carry the values from the user's own run; the structural trace carries the same shape of the same call without any of them, and the verb attaches it on its own)
|
|
68
|
+
- Never file in the turn that asked. (approval is the user's next message -- a yes assumed in the same turn is not one)
|
|
69
|
+
- Never edit the draft after showing it. (what was approved has to be what is filed, so a changed report is drafted again and shown again)
|
|
70
|
+
- Never open the URL or run the printed command. (submitting is the user's act, in their own browser or their own shell, under their own account)
|
|
71
|
+
- Never route a rule's refusal around the gate. (a field that cannot be filed is a field to rewrite, not to move somewhere the rule does not look)
|