agent-tune-kit 2.3.1__tar.gz → 2.3.3__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.
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/.codex-plugin/plugin.json +1 -1
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/PKG-INFO +16 -5
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/README.en.md +15 -4
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/README.md +15 -4
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/__init__.py +1 -1
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/trace.py +28 -1
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/visualize.py +8 -2
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/archive.py +3 -1
- agent_tune_kit-2.3.3/agent_tune_kit/traces/categories.py +173 -0
- agent_tune_kit-2.3.3/agent_tune_kit/traces/progress.py +95 -0
- agent_tune_kit-2.3.3/agent_tune_kit/traces/projects.py +201 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/records.py +80 -4
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/store.py +2 -1
- agent_tune_kit-2.3.3/agent_tune_kit/visualize/assets/shared/base.css +95 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/trace/app.js +121 -39
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/trace/page.html +3 -2
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/trace/styles.css +15 -74
- agent_tune_kit-2.3.3/agent_tune_kit/visualize/assets/traces/app.js +462 -0
- agent_tune_kit-2.3.3/agent_tune_kit/visualize/assets/traces/page.html +44 -0
- agent_tune_kit-2.3.3/agent_tune_kit/visualize/assets/traces/styles.css +109 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/trace.py +91 -35
- agent_tune_kit-2.3.3/agent_tune_kit/visualize/traces.py +270 -0
- agent_tune_kit-2.3.3/docs/trace-change-verification-readability-diagnosis.md +124 -0
- agent_tune_kit-2.3.3/docs/trace-dashboard-design.md +325 -0
- agent_tune_kit-2.3.3/docs/trace-project-attribution-design.md +244 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/trace-swimlane-design.md +4 -2
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/trace-tuning-validation.md +27 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/trace-tuning.md +129 -9
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/pyproject.toml +1 -1
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/validate_skill_pack.py +1 -1
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-trace-capture/SKILL.md +5 -2
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-trace-detect/SKILL.md +27 -3
- agent_tune_kit-2.3.3/skills/atk-trace-diagnose/SKILL.md +81 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-trace-init/SKILL.md +13 -1
- agent_tune_kit-2.3.3/skills/atk-trace-optimize/SKILL.md +62 -0
- agent_tune_kit-2.3.3/skills/atk-trace-verify/SKILL.md +63 -0
- agent_tune_kit-2.3.3/skills/atk-visualize-trace/SKILL.md +60 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_install_plugin.py +1 -1
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_release_scripts.py +1 -1
- agent_tune_kit-2.3.3/tests/test_trace_categories.py +217 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_trace_cli.py +7 -0
- agent_tune_kit-2.3.3/tests/test_trace_progress.py +132 -0
- agent_tune_kit-2.3.3/tests/test_trace_projects.py +57 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_trace_records.py +113 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_visualize_trace.py +54 -3
- agent_tune_kit-2.3.3/tests/test_visualize_traces.py +544 -0
- agent_tune_kit-2.3.3/tests/trace-dashboard.cjs +346 -0
- agent_tune_kit-2.3.3/tests/trace-stage-cards.cjs +117 -0
- agent_tune_kit-2.3.1/skills/atk-trace-diagnose/SKILL.md +0 -59
- agent_tune_kit-2.3.1/skills/atk-trace-optimize/SKILL.md +0 -49
- agent_tune_kit-2.3.1/skills/atk-trace-verify/SKILL.md +0 -50
- agent_tune_kit-2.3.1/skills/atk-visualize-trace/SKILL.md +0 -46
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/.gitignore +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/__main__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/dataset.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/evaluation.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/main.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/output.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/cli/results.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/datasets/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/datasets/prepare.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/datasets/truth.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/datasets/updates.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/datasets/validation.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/errors.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/execution/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/execution/calibration.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/execution/logs.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/execution/rules.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/execution/runner.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/execution/runtime.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/execution/tracing.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/installer.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/results/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/results/assessment.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/results/evidence.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/results/judgments.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/results/summary.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/results/versions.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/capture.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/langfuse.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/migrate.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/query.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/traces/segments.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/utils/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/utils/csv_io.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/utils/files.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/dataset/app.js +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/dataset/page.html +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/dataset/styles.css +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/dataset/vendor/echarts.min.js +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/failures/app.js +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/failures/page.html +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/failures/styles.css +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/trace/lanes.js +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/trace/vendor/markdown-it.LICENSE +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/assets/trace/vendor/markdown-it.min.js +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/dataset.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/failures.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/fields.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/history.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/render.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/visualize/timeline.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/agent_tune_kit/workspace.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/assets/agent_tune_kit_evaluation.png +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/assets/agent_tune_kit_trace.png +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/assets/arch-en.png +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/assets/arch.png +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/assets/architecture.en.svg +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/assets/architecture.svg +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/ato-atk-project-boundary-recommendation-2026-11-05.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/cli-skill-modularization-plan.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/codex_agent_tuning_prd.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/execution-evidence-and-tracing-plan.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/ground-truth-and-grading-assessment-2026-10-25.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/local-context.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/shared-versioning-and-confirmation.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/skill-template-pack-usage.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/surginame-atk-capability-review-2026-10-27.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/docs/truth-and-assessment.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/check-release.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/dev-refresh-install.sh +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/install_plugin.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/publish-pypi.sh +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/publish-release.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/release-version.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/scripts/release-version.sh +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-dataset/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-dataset/scripts/update_dataset.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-diagnose/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-eval/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-init/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-optimize/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-report/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-visualize-dataset/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-visualize-dataset/scripts/generate_dataset_browser.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-visualize-failures/SKILL.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/skills/atk-visualize-failures/scripts/generate_failure_browser.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/templates/.atk/runner/eval_runner.py.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/templates/.atk/runner/failure_rule.py.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/templates/.atk/runner/import_evidence.py.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/templates/.gitkeep +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/__init__.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/conftest.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/fixtures/legacy_eval_runner.py.txt +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/fixtures/managed_v1.py.md +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_assessment_contract.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_atk_init_contract.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_cli_workflow.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_dataset_updates.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_diagnosis.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_evidence_import.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_evidence_judgments.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_evidence_packaging.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_evidence_versions.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_execution_evidence.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_execution_outcomes.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_execution_tracing.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_generate_dataset_browser.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_generate_failure_browser.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_judge_calibration.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_local_context.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_plugin_cache.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_runner_output_columns.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_runner_template_row_logging.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_runtime_contracts.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_snapshot_reads.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_trace_capture.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_trace_langfuse.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_trace_query.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_trace_segments.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_trace_store.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_truth_browser.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_truth_cli.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_truth_contract.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_truth_runner.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/test_v2_progress.py +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/trace-event-wheel.cjs +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/trace-preview-scroll.cjs +0 -0
- {agent_tune_kit-2.3.1 → agent_tune_kit-2.3.3}/tests/trace-selection-reason.cjs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-tune-kit",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
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.
|
|
3
|
+
Version: 2.3.3
|
|
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
|
|
@@ -165,21 +165,28 @@ $atk-visualize-trace Open the execution lanes for session <session-id> and jump
|
|
|
165
165
|
|
|
166
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
167
|
|
|
168
|
-
|
|
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
169
|
|
|
170
|
-
|
|
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.
|
|
173
|
+
|
|
174
|
+
### 3. Investigate a Session or Specific Issues, Then Fix and Verify as Needed
|
|
171
175
|
|
|
172
176
|
```text
|
|
177
|
+
$atk-trace-diagnose Investigate session <session-id> and propose repair and verification plans.
|
|
173
178
|
$atk-trace-diagnose Investigate ISSUE-001 in session <session-id> and propose a repair and verification plan.
|
|
174
179
|
$atk-trace-optimize Implement the selected plan for ISSUE-001 in session <session-id>.
|
|
175
180
|
$atk-trace-verify Verify the candidate for ISSUE-001 in session <session-id> and check relevant behavior that should still work.
|
|
176
181
|
```
|
|
177
182
|
|
|
178
|
-
|
|
183
|
+
Without specified issues, diagnosis reviews all available session traces and works through existing and newly found anomalies, prioritizing causes directly related to project code, configuration, tool contracts, Prompt or Skill. Findings established as unrelated to the existing implementation may be skipped with an explanation. Findings with uncertain relevance remain open for investigation. Specified issues or concrete symptoms limit the investigation scope. You can start directly with diagnosis without a separate detection invocation.
|
|
184
|
+
|
|
185
|
+
Diagnoses and verification conclusions are saved per issue. If the evidence cannot establish which candidate loaded or whether key verification conditions were met, the verification outcome is `inconclusive`. Investigation results identify unread ranges and evidence gaps. As new issues surface, work through them individually or start another analysis with a new session trace.
|
|
179
186
|
|
|
180
187
|
### Where to Find Trace Outputs
|
|
181
188
|
|
|
182
|
-
Each session archive is stored under `.atk/traces/<session_id>/` in your Agent project. Files appear as you run the corresponding steps.
|
|
189
|
+
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/`.
|
|
183
190
|
|
|
184
191
|
| File or directory | What it shows |
|
|
185
192
|
| --- | --- |
|
|
@@ -188,3 +195,7 @@ Each session archive is stored under `.atk/traces/<session_id>/` in your Agent p
|
|
|
188
195
|
| `issues/<issue-id>/` | Issue evidence, diagnoses, and changes. Each verification's `result.json` records its status, conclusion, and evidence references |
|
|
189
196
|
|
|
190
197
|
[Trace tuning guide for source configuration, supported formats, and archive layout](docs/trace-tuning.md)
|
|
198
|
+
|
|
199
|
+
[Multi-project attribution and filtering proposal (not yet implemented, Chinese)](docs/trace-project-attribution-design.md)
|
|
200
|
+
|
|
201
|
+
Use `atk trace project` to register workspace projects and submodules. Issues can have multiple root-cause projects, filterable in the dashboard and session issue list. Repair details describe modification locations.
|
|
@@ -151,21 +151,28 @@ $atk-visualize-trace Open the execution lanes for session <session-id> and jump
|
|
|
151
151
|
|
|
152
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
153
|
|
|
154
|
-
|
|
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
155
|
|
|
156
|
-
|
|
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.
|
|
159
|
+
|
|
160
|
+
### 3. Investigate a Session or Specific Issues, Then Fix and Verify as Needed
|
|
157
161
|
|
|
158
162
|
```text
|
|
163
|
+
$atk-trace-diagnose Investigate session <session-id> and propose repair and verification plans.
|
|
159
164
|
$atk-trace-diagnose Investigate ISSUE-001 in session <session-id> and propose a repair and verification plan.
|
|
160
165
|
$atk-trace-optimize Implement the selected plan for ISSUE-001 in session <session-id>.
|
|
161
166
|
$atk-trace-verify Verify the candidate for ISSUE-001 in session <session-id> and check relevant behavior that should still work.
|
|
162
167
|
```
|
|
163
168
|
|
|
164
|
-
|
|
169
|
+
Without specified issues, diagnosis reviews all available session traces and works through existing and newly found anomalies, prioritizing causes directly related to project code, configuration, tool contracts, Prompt or Skill. Findings established as unrelated to the existing implementation may be skipped with an explanation. Findings with uncertain relevance remain open for investigation. Specified issues or concrete symptoms limit the investigation scope. You can start directly with diagnosis without a separate detection invocation.
|
|
170
|
+
|
|
171
|
+
Diagnoses and verification conclusions are saved per issue. If the evidence cannot establish which candidate loaded or whether key verification conditions were met, the verification outcome is `inconclusive`. Investigation results identify unread ranges and evidence gaps. As new issues surface, work through them individually or start another analysis with a new session trace.
|
|
165
172
|
|
|
166
173
|
### Where to Find Trace Outputs
|
|
167
174
|
|
|
168
|
-
Each session archive is stored under `.atk/traces/<session_id>/` in your Agent project. Files appear as you run the corresponding steps.
|
|
175
|
+
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/`.
|
|
169
176
|
|
|
170
177
|
| File or directory | What it shows |
|
|
171
178
|
| --- | --- |
|
|
@@ -174,3 +181,7 @@ Each session archive is stored under `.atk/traces/<session_id>/` in your Agent p
|
|
|
174
181
|
| `issues/<issue-id>/` | Issue evidence, diagnoses, and changes. Each verification's `result.json` records its status, conclusion, and evidence references |
|
|
175
182
|
|
|
176
183
|
[Trace tuning guide for source configuration, supported formats, and archive layout](docs/trace-tuning.md)
|
|
184
|
+
|
|
185
|
+
[Multi-project attribution and filtering proposal (not yet implemented, Chinese)](docs/trace-project-attribution-design.md)
|
|
186
|
+
|
|
187
|
+
Use `atk trace project` to register workspace projects and submodules. Issues can have multiple root-cause projects, filterable in the dashboard and session issue list. Repair details describe modification locations.
|
|
@@ -151,21 +151,28 @@ $atk-visualize-trace 打开 <session-id> 的执行泳道,从问题跳到相关
|
|
|
151
151
|
|
|
152
152
|
分析保留影响严重、反复出现、与你明确关注点相关,或有具体实现改进线索的问题,并说明保留理由、证据和检查范围。实现线索包括代码、配置、工具契约、Prompt 和 Skill;根因尚未确定也可进入后续诊断。轻微且无上述保留理由的观察不单独记录,没有保留问题不代表会话完全正确。
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
采集、分析、诊断、修复和验证 Skill 在阶段结束后默认生成或更新 `.atk/traces/<session_id>/preview.html`,不自动打开浏览器。需要打开时使用 `$atk-visualize-trace`;已打开的页面在文件更新后需刷新。初始化没有会话轨迹,不生成空页面。
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
这些阶段同时刷新项目大盘 `.atk/traces/index.html`,按会话或异常分类查看全部会话的问题和实际进度,并可跳回各会话页。需要打开时发送 `$atk-visualize-trace 打开轨迹大盘`。早于分类功能的问题显示为“未分类”,对应会话重新分析时补上。
|
|
157
|
+
|
|
158
|
+
可用 Trace ID 定位会话内的证据,在问题详情中查看保留理由。分享 HTML 前请核对其中留存的正文。
|
|
159
|
+
|
|
160
|
+
### 3. 调查会话或指定问题,按需修复和验证
|
|
157
161
|
|
|
158
162
|
```text
|
|
163
|
+
$atk-trace-diagnose 调查 <session-id>,给出修复和验证方案。
|
|
159
164
|
$atk-trace-diagnose 调查 <session-id> 的 ISSUE-001,给出修复和验证方案。
|
|
160
165
|
$atk-trace-optimize 实施 <session-id> 的 ISSUE-001 已选定的方案。
|
|
161
166
|
$atk-trace-verify 验证 <session-id> 的 ISSUE-001 对应候选,并检查相关正常行为。
|
|
162
167
|
```
|
|
163
168
|
|
|
164
|
-
|
|
169
|
+
未指定异常时,调查默认检查会话中可用的全部轨迹,逐项处理已记录和新发现的异常,优先定位与项目代码、配置、工具契约、Prompt 或 Skill 直接相关的原因。证据确认与现有实现无关的异常可以忽略,并说明理由。关联尚不明确的异常保留待查。指定异常或具体症状时,按指定范围调查。可以直接从调查开始,无需先单独执行检测。
|
|
170
|
+
|
|
171
|
+
诊断和验证结论分别按问题保存。证据不足以确认实际加载的候选或关键验证条件时,验证效果记为 `inconclusive`(无法判断)。调查结果会说明未检查的范围和证据缺口。后续发现新的问题,可继续按问题处理,或用新会话的轨迹开始下一次分析。
|
|
165
172
|
|
|
166
173
|
### 查看轨迹产物
|
|
167
174
|
|
|
168
|
-
会话归档保存在 Agent 项目的 `.atk/traces/<session_id>/`
|
|
175
|
+
会话归档保存在 Agent 项目的 `.atk/traces/<session_id>/` 下,文件随对应步骤生成。项目大盘 `index.html` 和分类表 `categories.json` 位于 `.atk/traces/` 根目录。
|
|
169
176
|
|
|
170
177
|
| 文件或目录 | 用途 |
|
|
171
178
|
| --- | --- |
|
|
@@ -174,3 +181,7 @@ $atk-trace-verify 验证 <session-id> 的 ISSUE-001 对应候选,并检查相
|
|
|
174
181
|
| `issues/<issue-id>/` | 查看问题证据、诊断和改动。各次验证的 `result.json` 记录验证状态、结论和证据引用 |
|
|
175
182
|
|
|
176
183
|
[轨迹调优指南,包含来源配置、支持格式和归档结构](docs/trace-tuning.md)
|
|
184
|
+
|
|
185
|
+
[多工程关联与项目筛选方案(待实现)](docs/trace-project-attribution-design.md)
|
|
186
|
+
|
|
187
|
+
支持通过 `atk trace project` 登记工作区及子模块;诊断可记录多个根因项目,大盘与会话问题栏按根因项目筛选。修改位置在修复详情中说明。
|
|
@@ -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,19 @@ 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
|
+
project = actions.add_parser("project", help="discover, list or update workspace projects")
|
|
38
|
+
project.add_argument("--project-root", default=".")
|
|
39
|
+
mode = project.add_mutually_exclusive_group(required=True)
|
|
40
|
+
mode.add_argument("--discover", action="store_true")
|
|
41
|
+
mode.add_argument("--list", action="store_true")
|
|
42
|
+
mode.add_argument("--data")
|
|
43
|
+
project.add_argument("--json", action="store_true")
|
|
44
|
+
category = actions.add_parser("category", help="list or update the project issue category vocabulary")
|
|
45
|
+
category.add_argument("--project-root", default=".")
|
|
46
|
+
mode = category.add_mutually_exclusive_group(required=True)
|
|
47
|
+
mode.add_argument("--list", action="store_true", help="list categories with per-session issue counts")
|
|
48
|
+
mode.add_argument("--data", help="category JSON; revising the vocabulary requires its current revision")
|
|
49
|
+
category.add_argument("--json", action="store_true")
|
|
37
50
|
for action in [
|
|
38
51
|
"migrate",
|
|
39
52
|
"fetch",
|
|
@@ -94,12 +107,25 @@ def register(subparsers):
|
|
|
94
107
|
parser.add_argument("--issue-id", required=True)
|
|
95
108
|
parser.add_argument("--file", required=True, help="root cause and repair/verification plan Markdown")
|
|
96
109
|
parser.add_argument("--diagnosis-id")
|
|
110
|
+
parser.add_argument("--expected-revision", type=int)
|
|
97
111
|
|
|
98
112
|
|
|
99
113
|
def execute(args):
|
|
114
|
+
if args.action == "project":
|
|
115
|
+
from agent_tune_kit.traces.projects import discover_projects, load_projects, save_project
|
|
116
|
+
|
|
117
|
+
if args.discover:
|
|
118
|
+
return discover_projects(args.project_root)
|
|
119
|
+
if args.list:
|
|
120
|
+
return load_projects(store.trace_root(args.project_root))
|
|
121
|
+
return save_project(args.project_root, read_json(args.data))
|
|
100
122
|
if args.action == "init":
|
|
101
123
|
text = Path(args.project_file).read_text(encoding="utf-8") if args.project_file else None
|
|
102
124
|
return store.init_workspace(args.project_root, config=read_json(args.config), project_text=text)
|
|
125
|
+
if args.action == "category":
|
|
126
|
+
if args.list:
|
|
127
|
+
return categories.list_categories(args.project_root)
|
|
128
|
+
return categories.save_category(args.project_root, read_json(args.data))
|
|
103
129
|
source = read_json(args.source)
|
|
104
130
|
if args.action == "migrate":
|
|
105
131
|
from agent_tune_kit.traces.migrate import migrate
|
|
@@ -152,6 +178,7 @@ def execute(args):
|
|
|
152
178
|
args.issue_id,
|
|
153
179
|
Path(args.file).read_text(encoding="utf-8"),
|
|
154
180
|
diagnosis_id=args.diagnosis_id,
|
|
181
|
+
expected_revision=args.expected_revision,
|
|
155
182
|
)
|
|
156
183
|
data = read_json(args.data)
|
|
157
184
|
if args.action == "issue":
|
|
@@ -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
|
-
|
|
35
|
-
|
|
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,
|
|
@@ -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
|
-
|
|
21
|
+
# Names of the files that live beside session directories at the archive root.
|
|
22
|
+
reserved = {"project.md", "config.json", "categories.json", "projects.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
|