agent-tune-kit 2.3.0__tar.gz → 2.3.2__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 (179) hide show
  1. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/.codex-plugin/plugin.json +1 -1
  2. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/PKG-INFO +14 -6
  3. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/README.en.md +13 -5
  4. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/README.md +13 -5
  5. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/__init__.py +1 -1
  6. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/trace.py +11 -1
  7. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/visualize.py +8 -2
  8. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/installer.py +47 -13
  9. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/archive.py +3 -1
  10. agent_tune_kit-2.3.2/agent_tune_kit/traces/categories.py +173 -0
  11. agent_tune_kit-2.3.2/agent_tune_kit/traces/progress.py +95 -0
  12. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/query.py +10 -7
  13. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/records.py +63 -1
  14. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/segments.py +3 -17
  15. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/store.py +1 -1
  16. agent_tune_kit-2.3.2/agent_tune_kit/visualize/assets/shared/base.css +95 -0
  17. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/trace/app.js +212 -55
  18. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/trace/page.html +2 -1
  19. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/trace/styles.css +28 -74
  20. agent_tune_kit-2.3.2/agent_tune_kit/visualize/assets/trace/vendor/markdown-it.LICENSE +158 -0
  21. agent_tune_kit-2.3.2/agent_tune_kit/visualize/assets/trace/vendor/markdown-it.min.js +13 -0
  22. agent_tune_kit-2.3.2/agent_tune_kit/visualize/assets/traces/app.js +403 -0
  23. agent_tune_kit-2.3.2/agent_tune_kit/visualize/assets/traces/page.html +41 -0
  24. agent_tune_kit-2.3.2/agent_tune_kit/visualize/assets/traces/styles.css +87 -0
  25. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/timeline.py +2 -12
  26. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/trace.py +99 -5
  27. agent_tune_kit-2.3.2/agent_tune_kit/visualize/traces.py +258 -0
  28. agent_tune_kit-2.3.2/docs/assets/agent_tune_kit_evaluation.png +0 -0
  29. agent_tune_kit-2.3.2/docs/assets/agent_tune_kit_trace.png +0 -0
  30. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/shared-versioning-and-confirmation.md +2 -2
  31. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/skill-template-pack-usage.md +10 -7
  32. agent_tune_kit-2.3.2/docs/trace-change-verification-readability-diagnosis.md +124 -0
  33. agent_tune_kit-2.3.2/docs/trace-dashboard-design.md +325 -0
  34. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/trace-swimlane-design.md +4 -2
  35. agent_tune_kit-2.3.2/docs/trace-tuning-validation.md +97 -0
  36. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/trace-tuning.md +128 -8
  37. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/pyproject.toml +1 -1
  38. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/check-release.py +1 -1
  39. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/dev-refresh-install.sh +1 -1
  40. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/validate_skill_pack.py +3 -1
  41. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-trace-capture/SKILL.md +5 -2
  42. agent_tune_kit-2.3.2/skills/atk-trace-detect/SKILL.md +92 -0
  43. agent_tune_kit-2.3.2/skills/atk-trace-diagnose/SKILL.md +62 -0
  44. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-trace-init/SKILL.md +1 -1
  45. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-trace-optimize/SKILL.md +6 -3
  46. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-trace-verify/SKILL.md +6 -3
  47. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-visualize-trace/SKILL.md +11 -7
  48. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/conftest.py +1 -1
  49. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_install_plugin.py +12 -9
  50. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_plugin_cache.py +88 -23
  51. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_release_scripts.py +1 -1
  52. agent_tune_kit-2.3.2/tests/test_trace_categories.py +217 -0
  53. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_trace_cli.py +9 -0
  54. agent_tune_kit-2.3.2/tests/test_trace_progress.py +132 -0
  55. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_trace_records.py +154 -0
  56. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_visualize_trace.py +194 -2
  57. agent_tune_kit-2.3.2/tests/test_visualize_traces.py +544 -0
  58. agent_tune_kit-2.3.2/tests/trace-dashboard.cjs +259 -0
  59. agent_tune_kit-2.3.2/tests/trace-selection-reason.cjs +41 -0
  60. agent_tune_kit-2.3.2/tests/trace-stage-cards.cjs +117 -0
  61. agent_tune_kit-2.3.0/docs/trace-tuning-validation.md +0 -56
  62. agent_tune_kit-2.3.0/skills/atk-trace-detect/SKILL.md +0 -57
  63. agent_tune_kit-2.3.0/skills/atk-trace-diagnose/SKILL.md +0 -47
  64. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/.gitignore +0 -0
  65. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/__init__.py +0 -0
  66. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/__main__.py +0 -0
  67. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/dataset.py +0 -0
  68. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/evaluation.py +0 -0
  69. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/main.py +0 -0
  70. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/output.py +0 -0
  71. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/cli/results.py +0 -0
  72. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/datasets/__init__.py +0 -0
  73. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/datasets/prepare.py +0 -0
  74. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/datasets/truth.py +0 -0
  75. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/datasets/updates.py +0 -0
  76. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/datasets/validation.py +0 -0
  77. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/errors.py +0 -0
  78. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/execution/__init__.py +0 -0
  79. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/execution/calibration.py +0 -0
  80. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/execution/logs.py +0 -0
  81. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/execution/rules.py +0 -0
  82. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/execution/runner.py +0 -0
  83. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/execution/runtime.py +0 -0
  84. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/execution/tracing.py +0 -0
  85. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/results/__init__.py +0 -0
  86. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/results/assessment.py +0 -0
  87. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/results/evidence.py +0 -0
  88. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/results/judgments.py +0 -0
  89. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/results/summary.py +0 -0
  90. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/results/versions.py +0 -0
  91. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/__init__.py +0 -0
  92. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/capture.py +0 -0
  93. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/langfuse.py +0 -0
  94. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/traces/migrate.py +0 -0
  95. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/utils/__init__.py +0 -0
  96. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/utils/csv_io.py +0 -0
  97. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/utils/files.py +0 -0
  98. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/__init__.py +0 -0
  99. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/dataset/app.js +0 -0
  100. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/dataset/page.html +0 -0
  101. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/dataset/styles.css +0 -0
  102. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/dataset/vendor/echarts.min.js +0 -0
  103. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/failures/app.js +0 -0
  104. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/failures/page.html +0 -0
  105. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/failures/styles.css +0 -0
  106. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/assets/trace/lanes.js +0 -0
  107. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/dataset.py +0 -0
  108. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/failures.py +0 -0
  109. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/fields.py +0 -0
  110. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/history.py +0 -0
  111. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/visualize/render.py +0 -0
  112. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/agent_tune_kit/workspace.py +0 -0
  113. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/assets/arch-en.png +0 -0
  114. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/assets/arch.png +0 -0
  115. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/assets/architecture.en.svg +0 -0
  116. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/assets/architecture.svg +0 -0
  117. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/ato-atk-project-boundary-recommendation-2026-11-05.md +0 -0
  118. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/cli-skill-modularization-plan.md +0 -0
  119. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/codex_agent_tuning_prd.md +0 -0
  120. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/execution-evidence-and-tracing-plan.md +0 -0
  121. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/ground-truth-and-grading-assessment-2026-10-25.md +0 -0
  122. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/local-context.md +0 -0
  123. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/surginame-atk-capability-review-2026-10-27.md +0 -0
  124. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/docs/truth-and-assessment.md +0 -0
  125. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/install_plugin.py +0 -0
  126. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/publish-pypi.sh +0 -0
  127. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/publish-release.py +0 -0
  128. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/release-version.py +0 -0
  129. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/scripts/release-version.sh +0 -0
  130. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-dataset/SKILL.md +0 -0
  131. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-dataset/scripts/update_dataset.py +0 -0
  132. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-diagnose/SKILL.md +0 -0
  133. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-eval/SKILL.md +0 -0
  134. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-init/SKILL.md +0 -0
  135. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-optimize/SKILL.md +0 -0
  136. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-report/SKILL.md +0 -0
  137. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-visualize-dataset/SKILL.md +0 -0
  138. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-visualize-dataset/scripts/generate_dataset_browser.py +0 -0
  139. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-visualize-failures/SKILL.md +0 -0
  140. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/skills/atk-visualize-failures/scripts/generate_failure_browser.py +0 -0
  141. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/templates/.atk/runner/eval_runner.py.md +0 -0
  142. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/templates/.atk/runner/failure_rule.py.md +0 -0
  143. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/templates/.atk/runner/import_evidence.py.md +0 -0
  144. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/templates/.gitkeep +0 -0
  145. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/__init__.py +0 -0
  146. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/fixtures/legacy_eval_runner.py.txt +0 -0
  147. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/fixtures/managed_v1.py.md +0 -0
  148. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_assessment_contract.py +0 -0
  149. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_atk_init_contract.py +0 -0
  150. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_cli_workflow.py +0 -0
  151. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_dataset_updates.py +0 -0
  152. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_diagnosis.py +0 -0
  153. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_evidence_import.py +0 -0
  154. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_evidence_judgments.py +0 -0
  155. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_evidence_packaging.py +0 -0
  156. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_evidence_versions.py +0 -0
  157. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_execution_evidence.py +0 -0
  158. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_execution_outcomes.py +0 -0
  159. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_execution_tracing.py +0 -0
  160. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_generate_dataset_browser.py +0 -0
  161. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_generate_failure_browser.py +0 -0
  162. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_judge_calibration.py +0 -0
  163. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_local_context.py +0 -0
  164. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_runner_output_columns.py +0 -0
  165. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_runner_template_row_logging.py +0 -0
  166. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_runtime_contracts.py +0 -0
  167. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_snapshot_reads.py +0 -0
  168. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_trace_capture.py +0 -0
  169. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_trace_langfuse.py +0 -0
  170. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_trace_query.py +0 -0
  171. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_trace_segments.py +0 -0
  172. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_trace_store.py +0 -0
  173. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_truth_browser.py +0 -0
  174. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_truth_cli.py +0 -0
  175. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_truth_contract.py +0 -0
  176. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_truth_runner.py +0 -0
  177. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/test_v2_progress.py +0 -0
  178. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/trace-event-wheel.cjs +0 -0
  179. {agent_tune_kit-2.3.0 → agent_tune_kit-2.3.2}/tests/trace-preview-scroll.cjs +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-tune-kit",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Local Codex plugin for batch Agent evaluation and session trace tuning with evidence-backed issues, focused changes, verification and offline review.",
5
5
  "author": {
6
6
  "name": "hustyichi",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agent-tune-kit
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Local Codex plugin for iterative Agent tuning with guided Skills, reusable runner templates, versioned results, and static validation.
5
5
  Project-URL: Homepage, https://github.com/hustyichi/agent-tune-kit
6
6
  Project-URL: Repository, https://github.com/hustyichi/agent-tune-kit
@@ -53,10 +53,12 @@ atk install
53
53
 
54
54
  </details>
55
55
 
56
- Finish active ATK tasks before reinstalling, and open a new session afterward to load updates. If you have installation problems, run `atk status` and confirm the plugin is enabled in Codex. [Installation and cache notes](docs/skill-template-pack-usage.md#local-plugin-install-and-smoke)
56
+ Finish active ATK tasks before reinstalling. `atk install` directly updates and verifies the Codex ATK cache, so no cache rebuild is needed afterward. Start a new session to replace instructions already read into a conversation; restart Codex if the skill list has not refreshed. If you have installation problems, run `atk status` and confirm the plugin is enabled in Codex. [Installation and cache notes](docs/skill-template-pack-usage.md#local-plugin-install-and-smoke)
57
57
 
58
58
  ## Evaluation-based tuning
59
59
 
60
+ ![Evaluation-based tuning architecture showing data preparation and Agent setup, batch evaluation, diagnosis, cross-round reporting, targeted tuning, and reruns.](docs/assets/agent_tune_kit_evaluation.png)
61
+
60
62
  Use a set of evaluation samples to find Agent failures and verify changes in later rounds. Prepare your Agent's entry point and a set of samples, preferably in CSV format. Five to twenty samples are enough to get started. Add expected answers, labels, or acceptance criteria where available.
61
63
 
62
64
  Open your Agent project in Codex and send the following `$atk-…` instructions in the conversation.
@@ -139,10 +141,10 @@ Each round saves results under `.atk/results/vN/` in your Agent project. Files a
139
141
 
140
142
  [More batch evaluation options and outputs](docs/skill-template-pack-usage.md#manual-22--26-loop)
141
143
 
142
- ![Evaluation-based tuning workflow, from preparing data and connecting an Agent through evaluation, diagnosis, reporting, tuning, and rerunning the evaluation.](docs/assets/architecture.en.svg)
143
-
144
144
  ## Continuous tuning from session traces
145
145
 
146
+ ![Trace tuning architecture showing session capture, issue detection, root-cause diagnosis, targeted fixes, and verification.](docs/assets/agent_tune_kit_trace.png)
147
+
146
148
  Start with an existing session and use its execution trace to check whether the Agent completed the task. Save diagnoses, fixes, and verification results by issue. Get the native Session ID, its source, and access to the trace, then open the Agent project in Codex. You can start without an evaluation dataset or runner.
147
149
 
148
150
  Run `atk trace --help` to check whether your CLI supports trace tuning. If the command is unavailable, follow the local source installation steps above with a version that supports trace tuning, then open a new Codex session.
@@ -161,7 +163,13 @@ $atk-trace-detect Check whether session <session-id> completed its task correctl
161
163
  $atk-visualize-trace Open the execution lanes for session <session-id> and jump from issues to the relevant trace segments and evidence fields.
162
164
  ```
163
165
 
164
- Detection lists issues, evidence, and what it inspected. Use Trace IDs to locate evidence within the session. You can generate a preview once traces are archived. Review any retained content in the HTML before sharing it.
166
+ Detection retains issues with serious impact, recurrence, direct relevance to your stated focus, or a concrete implementation improvement lead, and explains the retention reason, evidence and inspection scope. Implementation leads include code, configuration, tool contracts, Prompt and Skill; an unresolved cause can proceed to diagnosis. Minor observations with none of these reasons are not saved separately. No retained issues does not prove the session was correct.
167
+
168
+ The capture, detect, diagnose, optimize and verify Skills generate or update `.atk/traces/<session_id>/preview.html` at stage end without opening a browser. Use `$atk-visualize-trace` when you want to open it; refresh an already open page after the file changes. Initialization has no session trace and does not generate an empty page.
169
+
170
+ These stages also refresh the project dashboard `.atk/traces/index.html`, which shows issues and actual progress across all sessions by session or by issue category and links back to each session page. To open it, send `$atk-visualize-trace Open the trace dashboard`. Issues recorded before categories existed show as uncategorized until their session is analyzed again.
171
+
172
+ Use Trace IDs to locate evidence within the session and open issue details to read the retention reason. Review any retained content in the HTML before sharing it.
165
173
 
166
174
  ### 3. Investigate, Fix, and Verify an Issue as Needed
167
175
 
@@ -175,7 +183,7 @@ Start with diagnosis when you already have a specific issue and session evidence
175
183
 
176
184
  ### Where to Find Trace Outputs
177
185
 
178
- Each session archive is stored under `.atk/traces/<session_id>/` in your Agent project. Files appear as you run the corresponding steps.
186
+ Each session archive is stored under `.atk/traces/<session_id>/` in your Agent project. Files appear as you run the corresponding steps. The project dashboard `index.html` and the category list `categories.json` sit directly under `.atk/traces/`.
179
187
 
180
188
  | File or directory | What it shows |
181
189
  | --- | --- |
@@ -39,10 +39,12 @@ atk install
39
39
 
40
40
  </details>
41
41
 
42
- Finish active ATK tasks before reinstalling, and open a new session afterward to load updates. If you have installation problems, run `atk status` and confirm the plugin is enabled in Codex. [Installation and cache notes](docs/skill-template-pack-usage.md#local-plugin-install-and-smoke)
42
+ Finish active ATK tasks before reinstalling. `atk install` directly updates and verifies the Codex ATK cache, so no cache rebuild is needed afterward. Start a new session to replace instructions already read into a conversation; restart Codex if the skill list has not refreshed. If you have installation problems, run `atk status` and confirm the plugin is enabled in Codex. [Installation and cache notes](docs/skill-template-pack-usage.md#local-plugin-install-and-smoke)
43
43
 
44
44
  ## Evaluation-based tuning
45
45
 
46
+ ![Evaluation-based tuning architecture showing data preparation and Agent setup, batch evaluation, diagnosis, cross-round reporting, targeted tuning, and reruns.](docs/assets/agent_tune_kit_evaluation.png)
47
+
46
48
  Use a set of evaluation samples to find Agent failures and verify changes in later rounds. Prepare your Agent's entry point and a set of samples, preferably in CSV format. Five to twenty samples are enough to get started. Add expected answers, labels, or acceptance criteria where available.
47
49
 
48
50
  Open your Agent project in Codex and send the following `$atk-…` instructions in the conversation.
@@ -125,10 +127,10 @@ Each round saves results under `.atk/results/vN/` in your Agent project. Files a
125
127
 
126
128
  [More batch evaluation options and outputs](docs/skill-template-pack-usage.md#manual-22--26-loop)
127
129
 
128
- ![Evaluation-based tuning workflow, from preparing data and connecting an Agent through evaluation, diagnosis, reporting, tuning, and rerunning the evaluation.](docs/assets/architecture.en.svg)
129
-
130
130
  ## Continuous tuning from session traces
131
131
 
132
+ ![Trace tuning architecture showing session capture, issue detection, root-cause diagnosis, targeted fixes, and verification.](docs/assets/agent_tune_kit_trace.png)
133
+
132
134
  Start with an existing session and use its execution trace to check whether the Agent completed the task. Save diagnoses, fixes, and verification results by issue. Get the native Session ID, its source, and access to the trace, then open the Agent project in Codex. You can start without an evaluation dataset or runner.
133
135
 
134
136
  Run `atk trace --help` to check whether your CLI supports trace tuning. If the command is unavailable, follow the local source installation steps above with a version that supports trace tuning, then open a new Codex session.
@@ -147,7 +149,13 @@ $atk-trace-detect Check whether session <session-id> completed its task correctl
147
149
  $atk-visualize-trace Open the execution lanes for session <session-id> and jump from issues to the relevant trace segments and evidence fields.
148
150
  ```
149
151
 
150
- Detection lists issues, evidence, and what it inspected. Use Trace IDs to locate evidence within the session. You can generate a preview once traces are archived. Review any retained content in the HTML before sharing it.
152
+ Detection retains issues with serious impact, recurrence, direct relevance to your stated focus, or a concrete implementation improvement lead, and explains the retention reason, evidence and inspection scope. Implementation leads include code, configuration, tool contracts, Prompt and Skill; an unresolved cause can proceed to diagnosis. Minor observations with none of these reasons are not saved separately. No retained issues does not prove the session was correct.
153
+
154
+ The capture, detect, diagnose, optimize and verify Skills generate or update `.atk/traces/<session_id>/preview.html` at stage end without opening a browser. Use `$atk-visualize-trace` when you want to open it; refresh an already open page after the file changes. Initialization has no session trace and does not generate an empty page.
155
+
156
+ These stages also refresh the project dashboard `.atk/traces/index.html`, which shows issues and actual progress across all sessions by session or by issue category and links back to each session page. To open it, send `$atk-visualize-trace Open the trace dashboard`. Issues recorded before categories existed show as uncategorized until their session is analyzed again.
157
+
158
+ Use Trace IDs to locate evidence within the session and open issue details to read the retention reason. Review any retained content in the HTML before sharing it.
151
159
 
152
160
  ### 3. Investigate, Fix, and Verify an Issue as Needed
153
161
 
@@ -161,7 +169,7 @@ Start with diagnosis when you already have a specific issue and session evidence
161
169
 
162
170
  ### Where to Find Trace Outputs
163
171
 
164
- Each session archive is stored under `.atk/traces/<session_id>/` in your Agent project. Files appear as you run the corresponding steps.
172
+ Each session archive is stored under `.atk/traces/<session_id>/` in your Agent project. Files appear as you run the corresponding steps. The project dashboard `index.html` and the category list `categories.json` sit directly under `.atk/traces/`.
165
173
 
166
174
  | File or directory | What it shows |
167
175
  | --- | --- |
@@ -39,10 +39,12 @@ atk install
39
39
 
40
40
  </details>
41
41
 
42
- 重装前先结束正在运行的 ATK 任务,安装后新建会话以加载更新。遇到安装问题时,运行 `atk status` 检查安装,并在 Codex 中确认插件已启用。[安装与缓存说明](docs/skill-template-pack-usage.md#local-plugin-install-and-smoke)
42
+ 重装前先结束正在运行的 ATK 任务。`atk install` 会直接更新并校验 Codex 的 ATK 缓存,完成后无需等待缓存重建。已读入对话的旧指令仍需新建会话加载;技能列表未刷新时重启 Codex。遇到安装问题时,运行 `atk status` 检查安装,并在 Codex 中确认插件已启用。[安装与缓存说明](docs/skill-template-pack-usage.md#local-plugin-install-and-smoke)
43
43
 
44
44
  ## 评测调优
45
45
 
46
+ ![评测调优架构图,展示准备与接入、批量评测、判定与归因、报告与跨轮复核、定向优化及复测。](docs/assets/agent_tune_kit_evaluation.png)
47
+
46
48
  用一组评测样本找出 Agent 的问题,并用后续评测验证改动。准备 Agent 的入口和一组样本,推荐用 CSV,5 到 20 条即可开始。每条样本可附上期望答案、标签或可验收的行为标准。
47
49
 
48
50
  在 Codex 中打开 Agent 项目,在对话中发送下面的 `$atk-…` 指令。
@@ -125,10 +127,10 @@ $atk-report
125
127
 
126
128
  [更多批量评测运行选项和产物](docs/skill-template-pack-usage.md#manual-22--26-loop)
127
129
 
128
- ![评测调优流程,从准备数据和接入 Agent 到评测、诊断、报告、调优和复测。](docs/assets/architecture.svg)
129
-
130
130
  ## 轨迹持续调优
131
131
 
132
+ ![轨迹调优架构图,展示会话采集、问题识别、根因诊断、定向修复和执行验证。](docs/assets/agent_tune_kit_trace.png)
133
+
132
134
  从一个已有会话出发,结合实际执行轨迹判断任务是否完成,再按问题保存诊断、修复和验证记录。准备原始 Session ID、来源和可读取的轨迹,直接在 Codex 中打开 Agent 项目开始。无需评测数据集或 runner。
133
135
 
134
136
  可用 `atk trace --help` 检查当前 CLI 是否支持轨迹调优。如果命令不可用,按上面的本地源码方式安装支持轨迹调优的版本,再新建 Codex 会话。
@@ -147,7 +149,13 @@ $atk-trace-detect 分析 <session-id> 的任务结果是否正确,列出问题
147
149
  $atk-visualize-trace 打开 <session-id> 的执行泳道,从问题跳到相关轨迹片段和证据字段。
148
150
  ```
149
151
 
150
- 分析会列出问题、证据和检查范围。可用 Trace ID 定位会话内的证据。有归档轨迹即可生成预览,分享 HTML 前请核对其中留存的正文。
152
+ 分析保留影响严重、反复出现、与你明确关注点相关,或有具体实现改进线索的问题,并说明保留理由、证据和检查范围。实现线索包括代码、配置、工具契约、Prompt 和 Skill;根因尚未确定也可进入后续诊断。轻微且无上述保留理由的观察不单独记录,没有保留问题不代表会话完全正确。
153
+
154
+ 采集、分析、诊断、修复和验证 Skill 在阶段结束后默认生成或更新 `.atk/traces/<session_id>/preview.html`,不自动打开浏览器。需要打开时使用 `$atk-visualize-trace`;已打开的页面在文件更新后需刷新。初始化没有会话轨迹,不生成空页面。
155
+
156
+ 这些阶段同时刷新项目大盘 `.atk/traces/index.html`,按会话或异常分类查看全部会话的问题和实际进度,并可跳回各会话页。需要打开时发送 `$atk-visualize-trace 打开轨迹大盘`。早于分类功能的问题显示为“未分类”,对应会话重新分析时补上。
157
+
158
+ 可用 Trace ID 定位会话内的证据,在问题详情中查看保留理由。分享 HTML 前请核对其中留存的正文。
151
159
 
152
160
  ### 3. 按问题调查、修复和验证(按需)
153
161
 
@@ -161,7 +169,7 @@ $atk-trace-verify 验证 <session-id> 的 ISSUE-001 对应候选,并检查相
161
169
 
162
170
  ### 查看轨迹产物
163
171
 
164
- 会话归档保存在 Agent 项目的 `.atk/traces/<session_id>/` 下,文件随对应步骤生成。
172
+ 会话归档保存在 Agent 项目的 `.atk/traces/<session_id>/` 下,文件随对应步骤生成。项目大盘 `index.html` 和分类表 `categories.json` 位于 `.atk/traces/` 根目录。
165
173
 
166
174
  | 文件或目录 | 用途 |
167
175
  | --- | --- |
@@ -1,3 +1,3 @@
1
1
  """Agent Tune Kit package."""
2
2
 
3
- __version__ = "2.3.0"
3
+ __version__ = "2.3.2"
@@ -3,7 +3,7 @@
3
3
  import json
4
4
  from pathlib import Path
5
5
 
6
- from agent_tune_kit.traces import records, store
6
+ from agent_tune_kit.traces import categories, records, store
7
7
  from agent_tune_kit.traces.capture import fetch_session, import_session
8
8
  from agent_tune_kit.traces.query import overview, search, show
9
9
 
@@ -34,6 +34,12 @@ def register(subparsers):
34
34
  init.add_argument("--config", help="non-secret configuration JSON file")
35
35
  init.add_argument("--project-file", help="project context Markdown file")
36
36
  init.add_argument("--json", action="store_true")
37
+ category = actions.add_parser("category", help="list or update the project issue category vocabulary")
38
+ category.add_argument("--project-root", default=".")
39
+ mode = category.add_mutually_exclusive_group(required=True)
40
+ mode.add_argument("--list", action="store_true", help="list categories with per-session issue counts")
41
+ mode.add_argument("--data", help="category JSON; revising the vocabulary requires its current revision")
42
+ category.add_argument("--json", action="store_true")
37
43
  for action in [
38
44
  "migrate",
39
45
  "fetch",
@@ -100,6 +106,10 @@ def execute(args):
100
106
  if args.action == "init":
101
107
  text = Path(args.project_file).read_text(encoding="utf-8") if args.project_file else None
102
108
  return store.init_workspace(args.project_root, config=read_json(args.config), project_text=text)
109
+ if args.action == "category":
110
+ if args.list:
111
+ return categories.list_categories(args.project_root)
112
+ return categories.save_category(args.project_root, read_json(args.data))
103
113
  source = read_json(args.source)
104
114
  if args.action == "migrate":
105
115
  from agent_tune_kit.traces.migrate import migrate
@@ -4,6 +4,7 @@ import sys
4
4
  from agent_tune_kit.errors import UserActionRequired
5
5
  from agent_tune_kit.visualize import generate_dataset, generate_failures
6
6
  from agent_tune_kit.visualize.trace import generate_trace
7
+ from agent_tune_kit.visualize.traces import generate_traces
7
8
 
8
9
  from .output import emit
9
10
  from .trace import add_session_options, read_json
@@ -17,6 +18,8 @@ def add_options(parser, kind):
17
18
  parser.add_argument("--json", action="store_true")
18
19
  if kind == "dataset":
19
20
  parser.add_argument("--dataset-path", default=".atk/datasets/dataset.csv")
21
+ elif kind == "traces":
22
+ parser.add_argument("--project-root", default=".")
20
23
  elif kind == "trace":
21
24
  add_session_options(parser)
22
25
  parser.add_argument("--output-path")
@@ -31,14 +34,17 @@ def register(subparsers):
31
34
  commands = subparsers.add_parser("visualize", help="generate offline review pages").add_subparsers(
32
35
  dest="action", required=True
33
36
  )
34
- for kind in ["dataset", "failures", "trace"]:
35
- add_options(commands.add_parser(kind), kind)
37
+ helps = {"traces": "generate the project dashboard of every archived trace session"}
38
+ for kind in ["dataset", "failures", "trace", "traces"]:
39
+ add_options(commands.add_parser(kind, help=helps.get(kind)), kind)
36
40
 
37
41
 
38
42
  def execute(args):
39
43
  options = dict(overwrite=args.overwrite, open_browser=args.open_browser)
40
44
  if args.action == "dataset":
41
45
  return generate_dataset(args.dataset_path, **options)
46
+ if args.action == "traces":
47
+ return generate_traces(args.project_root, **options)
42
48
  if args.action == "trace":
43
49
  return generate_trace(
44
50
  args.project_root,
@@ -406,9 +406,8 @@ def cache_status(target: Path, cache: Path) -> tuple[bool, list[str]]:
406
406
  entries = sorted(cache.iterdir()) if cache.exists() else []
407
407
  entries = [entry for entry in entries if entry.name not in COPY_IGNORE_NAMES]
408
408
  if not entries:
409
- return True, ["缓存尚未生成,等待 Codex 加载;尚未确认会话生效。"]
409
+ return False, ["ATK 缓存缺失;运行 atk install 修复。"]
410
410
  expected = payload_hashes(target)
411
- version = current_manifest_version(target)
412
411
  ok = True
413
412
  lines: list[str] = []
414
413
  for entry in entries:
@@ -418,22 +417,57 @@ def cache_status(target: Path, cache: Path) -> tuple[bool, list[str]]:
418
417
  missing = sorted(expected.keys() - actual.keys())
419
418
  extra = sorted(actual.keys() - expected.keys())
420
419
  changed = sorted(key for key in expected.keys() & actual.keys() if expected[key] != actual[key])
421
- version_ok = entry.name in (version, "local")
422
- if not (missing or extra or changed) and version_ok:
420
+ if not (missing or extra or changed):
423
421
  lines.append(f"缓存与安装内容一致:{entry}")
424
422
  continue
425
423
  ok = False
426
424
  lines.append(f"缓存不一致:{entry}(修改 {len(changed)},缺失 {len(missing)},多余 {len(extra)})")
427
- if not version_ok:
428
- lines.append(f" 缓存目录版本 {entry.name} 与安装版本 {version} 不同。")
429
425
  differences = [("修改", changed), ("缺失", missing), ("多余", extra)]
430
426
  details = [f" {label}:{name}" for label, names in differences for name in names]
431
427
  lines.extend(details[:5])
432
428
  if not ok:
433
- lines.append("运行 atk install 刷新缓存,然后重启 Codex 并新建会话。")
429
+ lines.append("运行 atk install 更新缓存;已读入的旧指令需新建会话,技能列表未刷新时重启 Codex。")
434
430
  return ok, lines
435
431
 
436
432
 
433
+ def refresh_plugin_cache(target: Path, cache: Path, payload_source: PayloadSource) -> None:
434
+ entries = [entry for entry in cache.iterdir() if entry.name not in COPY_IGNORE_NAMES] if cache.exists() else []
435
+ for entry in entries:
436
+ if entry.is_symlink() or not entry.is_dir():
437
+ raise InstallError(f"unsafe plugin cache entry: {entry}")
438
+
439
+ cache.parent.mkdir(parents=True, exist_ok=True)
440
+ work = Path(tempfile.mkdtemp(prefix=f".{PLUGIN_NAME}-", dir=cache.parent))
441
+ staged, previous = work / "new", work / "previous"
442
+ try:
443
+ copy_payload_tree(payload_source, staged / "local")
444
+ # Keep paths already advertised to sessions readable with the new payload.
445
+ for entry in entries:
446
+ if entry.name != "local":
447
+ shutil.copytree(staged / "local", staged / entry.name)
448
+ valid, details = cache_status(target, staged)
449
+ if not valid:
450
+ raise InstallError("staged cache validation failed: " + "; ".join(details))
451
+
452
+ # shortcut: two renames leave a brief gap; use a platform directory exchange if live-read continuity is required.
453
+ if cache.exists():
454
+ os.replace(cache, previous)
455
+ try:
456
+ os.replace(staged, cache)
457
+ except BaseException:
458
+ if previous.exists():
459
+ try:
460
+ os.replace(previous, cache)
461
+ except OSError as exc:
462
+ raise InstallError(f"cache restore failed; previous cache preserved at {previous}: {exc}") from exc
463
+ raise
464
+ remove_path(previous)
465
+ finally:
466
+ # A failed restore must not delete the only surviving previous cache.
467
+ if not previous.exists():
468
+ remove_path(work)
469
+
470
+
437
471
  def collect_status(
438
472
  marketplace_path: Path, plugin_store: Path, payload_source: PayloadSource | None = None
439
473
  ) -> tuple[dict[str, bool], list[str]]:
@@ -561,7 +595,7 @@ def run_preview(args: argparse.Namespace) -> int:
561
595
  print("已有文件或注册与本次安装冲突;替换需要确认。")
562
596
  if args.smoke:
563
597
  smoke_check(marketplace_path, plugin_store, dry_run=True, payload_source=payload_source)
564
- print(f"安装校验成功后将清理 ATK 缓存:{cache}")
598
+ print(f"安装校验成功后将更新 ATK 缓存:{cache}")
565
599
  print("未修改文件。运行 atk install 进行安装。")
566
600
  return 0
567
601
 
@@ -595,16 +629,16 @@ def run_install(args: argparse.Namespace) -> int:
595
629
  smoke_check(marketplace_path, plugin_store, dry_run=False, payload_source=payload_source)
596
630
 
597
631
  validate_manifest(target / ".codex-plugin" / "plugin.json")
598
- # Invalidate only after a usable installation exists, including with --no-smoke.
632
+ # Publish only after a usable installation exists, including with --no-smoke.
599
633
  try:
600
634
  cache = plugin_cache_path(target, marketplace_path, payload_source)
601
- remove_path(cache)
635
+ refresh_plugin_cache(target, cache, payload_source)
602
636
  except (InstallError, OSError) as exc:
603
637
  raise InstallError(f"plugin installed, but cache refresh failed: {cache}: {exc}") from exc
604
638
 
605
639
  print(f"已为 Codex 本地安装 Agent Tune Kit {payload_source.package_version}。")
606
- print("ATK 缓存已清理或尚未生成;等待 Codex 重新加载。")
607
- print("下一步:刷新 Codex /plugins,确认启用 Agent Tune Kit;重启 Codex 并新建会话。")
640
+ print("ATK 缓存已更新并校验,后续文件读取使用最新内容。")
641
+ print("下一步:确认 Codex /plugins 已启用 Agent Tune Kit;已读入的旧指令需新建会话,技能列表未刷新时重启 Codex。")
608
642
  return 0
609
643
 
610
644
 
@@ -691,7 +725,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
691
725
  subparsers.add_parser(
692
726
  "preview", parents=[common], help="preview planned marketplace/plugin-store changes without writing"
693
727
  )
694
- subparsers.add_parser("install", parents=[common], help="install, validate, then clear personal ATK caches")
728
+ subparsers.add_parser("install", parents=[common], help="install, validate, then replace personal ATK caches")
695
729
  subparsers.add_parser(
696
730
  "status", parents=[common], help="check local installation and compare cached plugin contents (read-only)"
697
731
  )
@@ -18,7 +18,9 @@ def component(value):
18
18
  if not isinstance(value, str) or not value.strip():
19
19
  raise UserActionRequired("An explicit nonempty native ID is required.")
20
20
  name = quote(value, safe="")
21
- if name.startswith(".") or name.casefold() in {"project.md", "config.json", "_export_summary"}:
21
+ # Names of the files that live beside session directories at the archive root.
22
+ reserved = {"project.md", "config.json", "categories.json", "index.html", "_export_summary"}
23
+ if name.startswith(".") or name.casefold() in reserved:
22
24
  name = "%" + format(ord(name[0]), "02X") + name[1:]
23
25
  if len(name) > 200:
24
26
  name = name[:150] + "~" + hashlib.sha256(value.encode()).hexdigest()
@@ -0,0 +1,173 @@
1
+ """Project-level issue category vocabulary.
2
+
3
+ The host decides which category fits an issue; this module only keeps the vocabulary consistent. Entries are never
4
+ deleted, so issues that reference a category stay displayable after it is deprecated.
5
+ """
6
+
7
+ import re
8
+ from pathlib import Path
9
+
10
+ from agent_tune_kit.errors import UserActionRequired
11
+ from agent_tune_kit.results.evidence import now, safe_path, write_manifest
12
+
13
+ from .store import ensure_workspace, read_json, session_write_lock, trace_root
14
+
15
+ FILENAME = "categories.json"
16
+ CATEGORY_ID = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*")
17
+ MAX_ID = 64
18
+
19
+
20
+ def _path(root):
21
+ return safe_path(Path(root), FILENAME)
22
+
23
+
24
+ def _check_catalog(value):
25
+ if (
26
+ value.get("schema_version") != 1
27
+ or not isinstance(value.get("revision"), int)
28
+ or isinstance(value.get("revision"), bool)
29
+ or not isinstance(value.get("categories"), list)
30
+ ):
31
+ raise UserActionRequired("Invalid Trace category vocabulary.")
32
+ seen = set()
33
+ for item in value["categories"]:
34
+ if (
35
+ not isinstance(item, dict)
36
+ or not isinstance(item.get("id"), str)
37
+ or not CATEGORY_ID.fullmatch(item["id"])
38
+ or item["id"] in seen
39
+ or any(not isinstance(item.get(key), str) or not item[key].strip() for key in ("name", "definition"))
40
+ or not isinstance(item.get("deprecated"), bool)
41
+ ):
42
+ raise UserActionRequired("Invalid Trace category entry.")
43
+ seen.add(item["id"])
44
+ return value
45
+
46
+
47
+ def load_catalog(root):
48
+ """Read the vocabulary from a traces root; a missing file is an empty vocabulary."""
49
+ path = _path(root)
50
+ if not path.exists():
51
+ return {"schema_version": 1, "revision": 0, "categories": []}
52
+ return _check_catalog(read_json(path))
53
+
54
+
55
+ def category_map(root):
56
+ """Return ``{id: entry}`` for display, tolerating a damaged file so one bad record cannot hide sessions."""
57
+ try:
58
+ return {item["id"]: item for item in load_catalog(root)["categories"]}
59
+ except UserActionRequired:
60
+ return {}
61
+
62
+
63
+ def check_issue_category(root, category, previous=None):
64
+ """Validate an issue category against the vocabulary; an unchanged deprecated value stays valid."""
65
+ if not isinstance(category, str) or not category:
66
+ raise UserActionRequired("Issue category must be a category ID from the project vocabulary.")
67
+ entry = next((item for item in load_catalog(root)["categories"] if item["id"] == category), None)
68
+ if entry is None:
69
+ raise UserActionRequired(
70
+ f"Unknown issue category {category!r}; list the vocabulary with `atk trace category --list` first."
71
+ )
72
+ if entry["deprecated"] and category != previous:
73
+ raise UserActionRequired(f"Issue category {category!r} is deprecated; choose a current category.")
74
+ return category
75
+
76
+
77
+ def save_category(project_root, data):
78
+ """Add or revise one category. Supplying ``revision`` is required once the vocabulary exists."""
79
+ if not isinstance(data, dict):
80
+ raise UserActionRequired("Category data must be an object.")
81
+ unknown = set(data) - {"id", "name", "definition", "deprecated", "revision", "expected_revision"}
82
+ if unknown:
83
+ raise UserActionRequired(f"Unsupported category fields: {', '.join(sorted(unknown))}.")
84
+ category_id = data.get("id")
85
+ if not isinstance(category_id, str) or not CATEGORY_ID.fullmatch(category_id) or len(category_id) > MAX_ID:
86
+ raise UserActionRequired("Category id must be lowercase kebab-case (letters, digits and single hyphens).")
87
+ expected = data.get("revision", data.get("expected_revision"))
88
+ if expected is not None and (not isinstance(expected, int) or isinstance(expected, bool)):
89
+ raise UserActionRequired("Category revision must be an integer.")
90
+ # The vocabulary lives in the private archive, so it gets the same Git ignore rule as captured sessions.
91
+ root, _ = ensure_workspace(project_root)
92
+ with session_write_lock(root):
93
+ catalog = load_catalog(root)
94
+ if expected is None and catalog["revision"] == 0:
95
+ expected = 0
96
+ if expected != catalog["revision"]:
97
+ raise UserActionRequired(
98
+ f"Stale or missing category revision; current vocabulary revision is {catalog['revision']}."
99
+ )
100
+ entries = catalog["categories"]
101
+ existing = next((item for item in entries if item["id"] == category_id), None)
102
+ stamp = now()
103
+ if existing is None:
104
+ for key in ("name", "definition"):
105
+ if not isinstance(data.get(key), str) or not data[key].strip():
106
+ raise UserActionRequired(f"A new category needs a nonempty {key}.")
107
+ if data.get("deprecated") not in (None, False):
108
+ raise UserActionRequired("A new category cannot start deprecated.")
109
+ entry = {
110
+ "id": category_id,
111
+ "name": data["name"].strip(),
112
+ "definition": data["definition"].strip(),
113
+ "deprecated": False,
114
+ "created_at": stamp,
115
+ "updated_at": stamp,
116
+ }
117
+ entries.append(entry)
118
+ else:
119
+ entry = existing
120
+ for key in ("name", "definition"):
121
+ if key in data:
122
+ if not isinstance(data[key], str) or not data[key].strip():
123
+ raise UserActionRequired(f"Category {key} must be a nonempty string.")
124
+ entry[key] = data[key].strip()
125
+ if "deprecated" in data:
126
+ if not isinstance(data["deprecated"], bool):
127
+ raise UserActionRequired("Category deprecated must be true or false.")
128
+ entry["deprecated"] = data["deprecated"]
129
+ entry["updated_at"] = stamp
130
+ catalog["revision"] += 1
131
+ write_manifest(_path(root), catalog)
132
+ return {**entry, "vocabulary_revision": catalog["revision"], "created": existing is None}
133
+
134
+
135
+ def list_categories(project_root):
136
+ """Return the vocabulary with how many issues in each session reference every category."""
137
+ from . import records
138
+
139
+ root = trace_root(project_root)
140
+ catalog = load_catalog(root)
141
+ usage = {item["id"]: {} for item in catalog["categories"]}
142
+ uncategorized = 0
143
+ warnings = []
144
+ sessions = sorted(child for child in root.iterdir() if child.is_dir()) if root.exists() else []
145
+ for session_dir in sessions:
146
+ if not (session_dir / "session.json").is_file() or session_dir.is_symlink():
147
+ continue
148
+ issue_root = session_dir / "issues"
149
+ for issue_file in sorted(issue_root.glob("*/issue.json")) if issue_root.is_dir() else []:
150
+ try:
151
+ issue = records.load_issue(session_dir, issue_file.parent.name)
152
+ except UserActionRequired as exc:
153
+ warnings.append({"session": session_dir.name, "issue_id": issue_file.parent.name, "error": str(exc)})
154
+ continue
155
+ category = issue.get("category")
156
+ if category is None:
157
+ uncategorized += 1
158
+ elif category in usage:
159
+ usage[category][session_dir.name] = usage[category].get(session_dir.name, 0) + 1
160
+ return {
161
+ "revision": catalog["revision"],
162
+ "categories": [
163
+ {
164
+ **item,
165
+ "sessions": len(usage[item["id"]]),
166
+ "issues": sum(usage[item["id"]].values()),
167
+ "by_session": usage[item["id"]],
168
+ }
169
+ for item in catalog["categories"]
170
+ ],
171
+ "uncategorized_issues": uncategorized,
172
+ "warnings": warnings,
173
+ }
@@ -0,0 +1,95 @@
1
+ """Actual issue progress derived from saved records.
2
+
3
+ The declared issue ``status`` is not a progress signal; only ``excluded`` is honored so that withdrawn issues do not
4
+ stay in the backlog. Everything else comes from diagnoses, changes and verification records. The functions here do no
5
+ I/O so the session preview and the project dashboard report the same result.
6
+ """
7
+
8
+ from datetime import datetime
9
+
10
+ # Workflow order: unfinished work first, closed outcomes last. Keys double as CSS class names.
11
+ PROGRESS = (
12
+ ("pending", "待诊断"),
13
+ ("diagnosed", "已诊断"),
14
+ ("changing", "修复中"),
15
+ ("awaiting", "待验证"),
16
+ ("verifying", "验证中"),
17
+ ("fail", "验证失败"),
18
+ ("inconclusive", "无法判断"),
19
+ ("pass", "验证通过"),
20
+ ("excluded", "已排除"),
21
+ )
22
+ LABELS = dict(PROGRESS)
23
+ ORDER = {key: index for index, (key, _) in enumerate(PROGRESS)}
24
+
25
+
26
+ def parse_instant(value):
27
+ """Return epoch seconds for an ISO timestamp, or None when it is missing, malformed or has no timezone."""
28
+ try:
29
+ stamp = datetime.fromisoformat(value.replace("Z", "+00:00"))
30
+ if stamp.tzinfo is not None:
31
+ return stamp.timestamp()
32
+ except (TypeError, ValueError, AttributeError, OverflowError, OSError):
33
+ pass
34
+ return None
35
+
36
+
37
+ def instant(record):
38
+ """Return the creation instant in seconds, or infinity when it is missing or has no timezone."""
39
+ when = parse_instant(record.get("created_at")) if isinstance(record, dict) else None
40
+ return float("inf") if when is None else when
41
+
42
+
43
+ def _recency(record):
44
+ """Sort key where the newest known instant is largest; records with unknown time never count as newest."""
45
+ when = instant(record)
46
+ known = when != float("inf")
47
+ return known, when if known else 0.0, str(record.get("id", ""))
48
+
49
+
50
+ def _result(key):
51
+ return {"key": key, "label": LABELS[key]}
52
+
53
+
54
+ # Verification states whose per-issue conclusions count as an effect outcome. Failed, blocked and interrupted attempts
55
+ # stay visible on the session page but never replace an earlier finished outcome.
56
+ FINISHED = {"completed", "partial"}
57
+
58
+
59
+ def issue_progress(issue, changes):
60
+ """Return ``{key, label}`` for one issue.
61
+
62
+ ``changes`` pairs every change linked to the issue (including a shared change owned by another issue) with all
63
+ verification records of that change; an entry that could not be read is passed as ``None``. The newest completed
64
+ or running change decides the stage, because an older verified attempt says nothing about a newer one. Within that
65
+ change the newest finished verification gives the outcome. An unreadable verification might be the newest one, so
66
+ the outcome is then unknown rather than an older result.
67
+ """
68
+ if issue.get("status") == "excluded":
69
+ return _result("excluded")
70
+ active = [(change, items) for change, items in changes if change.get("status") in {"completed", "running"}]
71
+ if active:
72
+ change, verifications = max(active, key=lambda pair: _recency(pair[0]))
73
+ if change["status"] == "running":
74
+ return _result("changing")
75
+ if any(not isinstance(item, dict) for item in verifications):
76
+ return _result("inconclusive")
77
+ finished = [item for item in verifications if item.get("status") in FINISHED]
78
+ if finished:
79
+ latest = max(finished, key=_recency)
80
+ conclusions = latest.get("conclusions")
81
+ conclusion = conclusions.get(issue.get("id")) if isinstance(conclusions, dict) else None
82
+ outcome = conclusion.get("outcome") if isinstance(conclusion, dict) else None
83
+ return _result(outcome if outcome in {"pass", "fail"} else "inconclusive")
84
+ if any(item.get("status") == "running" for item in verifications):
85
+ return _result("verifying")
86
+ return _result("awaiting")
87
+ return _result("diagnosed" if issue.get("diagnoses") else "pending")
88
+
89
+
90
+ def summarize(keys):
91
+ """Count progress keys in workflow order, omitting nothing so callers can render stable legends."""
92
+ counts = dict.fromkeys(LABELS, 0)
93
+ for key in keys:
94
+ counts[key] += 1
95
+ return counts