xrefkit 0.7.1__tar.gz → 0.7.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.
- {xrefkit-0.7.1/xrefkit.egg-info → xrefkit-0.7.3}/PKG-INFO +1 -1
- {xrefkit-0.7.1 → xrefkit-0.7.3}/pyproject.toml +1 -1
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_ci_workflow_invariants.py +67 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_cli.py +7 -1
- xrefkit-0.7.3/tests/test_compact_skill_logs.py +190 -0
- xrefkit-0.7.3/tests/test_contribution_adoption_bytes.py +240 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_contribution_returns.py +148 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_runtime_audit.py +435 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/__init__.py +1 -1
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/catalog.py +8 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/contribution_returns.py +12 -8
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/operations_cli.py +13 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/skillrun.py +388 -33
- {xrefkit-0.7.1 → xrefkit-0.7.3/xrefkit.egg-info}/PKG-INFO +1 -1
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit.egg-info/SOURCES.txt +2 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/LICENSE +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/README.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/setup.cfg +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_artifact_selected_family.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_attention_pet.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_attention_pet_client_protocol.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_attention_pet_codex_session.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_attention_pet_fit.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_azure_connection.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_azure_import.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_azure_recovery.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_azure_update_candidate.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_azure_writer.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_boundary_analysis.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_brownfield_csharp_structure_definitions.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_brownfield_file_editing_protocol.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_business_intake_conversation_definitions.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_business_intake_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_calibration_lint.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_catalog_preparation_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_check_skill_knowledge_xids.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_code_constraint_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_collect_analyzer_sarif.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_constraint_derivation_family_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_constraint_derivation_selected_family.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_convert_to_xrefkit_skill.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_cs_scope_probe.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_csharp_commonality.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_csharp_naming_profile.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_ctx.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_cutover_readiness.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_dashboard.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_database_batch_definition_migration.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_decision_trace.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_design_business_intake_selected_family.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_design_planning_definition_migration.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_dotnet_definition_candidate.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_editorial_family_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_editorial_intake_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_editorial_selected_family.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_embedded_startup_pack.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_error_policy_audit.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_error_policy_locator.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_execution_binding.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_existing_skill_definition_batch.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_final_skilldefinition_batch.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_flow_selected_family.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_fm_multiroot.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_gate.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_gateway.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_gateway_mcp.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_gateway_skill_adapter.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_gateway_work_items.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_goal_desired_state.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_governance_entry.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_host_precheck.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_human_evaluation.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_implementation_flow_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_implementation_review_selected_family.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_inbound_uploads.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_instruction_workflow.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_knowledge_relations_validator.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_language_review_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_legacy_guidance.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_legacy_migration.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_legacy_resources.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_mcp_package_routing.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_mcp_setup.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_mcp_subagent_startup.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_mcp_subagent_startup_integration.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_ontology_assessment_validator.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_os_family_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_os_knowledge_family_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_os_selected_family.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_ownership.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_packmeta.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_plan_markdown.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_plan_observation.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_project_quality_baseline.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_qa_report_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_reporting_profiles.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_reporting_quality.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_repository_skill_adoption.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_repository_startup_pack.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_resource_provider.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_runtime_contracts.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_sarif_to_locator.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_security_definition_migration.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_site_release_metadata.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_definition_governance.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_definition_mcp.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_definition_runtime.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_edits.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_maturity_return.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skill_release_compatibility.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skillmeta.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_skills_sync.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_source_analysis_fragments.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_structure_catalog.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_subagent_startup.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_subagent_startup_boundaries.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_wbs.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_work_management.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_work_packages.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_workflow_family_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_xref.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_xrefkit_instance.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_xrefkit_tools.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_xrefkit_v2_discovery.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_xrefkit_v2_models.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/tests/test_xrefkit_v2_pipeline.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/__main__.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/__init__.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/__main__.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/client.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/client_protocol.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/codex_session.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/evaluator.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/model.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/presentation.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/profiles.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/scenarios.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/server.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/attention_pet/store.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/azure_connection.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/azure_import.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/azure_recovery.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/azure_update_candidate.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/azure_writer.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/boundary_analysis.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/catalog_cli.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/cli.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/contracts.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/ctx.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/dashboard.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/decision_trace.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/discovery.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/execution_binding.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/execution_binding_cli.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/gate.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/gateway.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/goalstate.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/governance_entry.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/governance_entry_cli.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/hashing.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/host_precheck.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/import_skill.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/instance.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/loaders.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/__init__.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/audit.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/bootstrap.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/cli.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/client_cache.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/client_flow.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/context_registry.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/context_token.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/contracts.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/contribution_adoption.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/dist.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/gateway.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/governed_overlays.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/inbound_uploads.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/knowledge_edits.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/legacy_guidance.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/legacy_migration.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/legacy_resources.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/ownership.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/repository.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/schemas.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/server.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/setup.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/skill_edits.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/skill_maturity.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/startup_contract_pack.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/subagent_startup.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp/update_gate_intake.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/mcp_tools.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/__init__.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/common.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/effective_bundle.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/human_evaluation.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/local_manifest.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/package_manifest.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/run_log.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/server_config.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/models/skill_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/ownership.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/packmeta.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/plan_markdown.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/plan_observation.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/registry.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/reporting.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/reporting_profiles.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/repository_skills.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resolver.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resource_provider.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/attention_pet/client-state.schema.json +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/attention_pet/pet.css +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/attention_pet/pet.html +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/attention_pet/pet.js +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/contracts.json +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/current.json +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/model_body.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/startup_sources/000_agent_entry.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/startup_sources/011_startup_xref_routing.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/startup_sources/015_shared_memory_operations.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/startup_sources/016_uncertainty_protocol.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/startup_sources/017_base_and_xref_layering.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/startup_sources/053_context_direction_security_guard.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/base/startup_sources/yomiyasu_LICENSE.txt +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/migration/docs/guides/062_legacy_flow_skill_migration_guide.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/migration/knowledge/operations/130_legacy_flow_skill_migration_rules.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/migration/knowledge/organization/160_context_direction_guard_rules.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/migration/skills/os/legacy_flow_skill_migration/SKILL.v1.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/migration/skills/os/legacy_flow_skill_migration/references/legacy_flow_skill_migration_template.md +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/migration/skills/repository_adoption.json +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/resources/migration/tools/migrate_legacy_flow_skill.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/runlog.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/skill_definition.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/skill_definition_catalog.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/skill_definition_governance.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/skillmeta.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/skills_sync.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/structure_catalog.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/subagent_startup.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/tools/__init__.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/tools/__main__.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/v2_cli.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/wbs.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/work_management.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/work_management_view.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/workspace.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit/xref.py +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit.egg-info/dependency_links.txt +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit.egg-info/entry_points.txt +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit.egg-info/requires.txt +0 -0
- {xrefkit-0.7.1 → xrefkit-0.7.3}/xrefkit.egg-info/top_level.txt +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""CI cleanup must retain release coverage and only cancel obsolete PR runs."""
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
3
5
|
|
|
4
6
|
import pytest
|
|
5
7
|
import yaml
|
|
@@ -16,6 +18,36 @@ PACKAGES = {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
@pytest.mark.parametrize("stop_status,remains,expected", [
|
|
22
|
+
(0, False, 0), (1, False, 0), (1, True, 1), (2, False, 2),
|
|
23
|
+
])
|
|
24
|
+
def test_core_updater_stop_handles_exit_race_without_hiding_errors(stop_status, remains, expected):
|
|
25
|
+
bash = shutil.which("bash")
|
|
26
|
+
if Path("C:/Program Files/Git/bin/bash.exe").is_file():
|
|
27
|
+
bash = "C:/Program Files/Git/bin/bash.exe"
|
|
28
|
+
if not bash:
|
|
29
|
+
pytest.skip("Bash is required to execute the workflow stop block")
|
|
30
|
+
steps = workflow("python-package.yml")["jobs"]["artifact-inspection"]["steps"]
|
|
31
|
+
update = next(s["run"] for s in steps if "sudo apt-get install --yes clamav" in s.get("run", ""))
|
|
32
|
+
block = update.split("# Package installation", 1)[1].split("# A failed definition update", 1)[0]
|
|
33
|
+
block = "# Package installation" + block
|
|
34
|
+
mocks = f"""
|
|
35
|
+
set -euo pipefail
|
|
36
|
+
calls=0
|
|
37
|
+
pgrep() {{
|
|
38
|
+
calls=$((calls + 1))
|
|
39
|
+
if [ "$calls" -eq 1 ]; then return 0; fi
|
|
40
|
+
return {0 if remains else 1}
|
|
41
|
+
}}
|
|
42
|
+
sudo() {{ return {stop_status}; }}
|
|
43
|
+
sleep() {{ :; }}
|
|
44
|
+
"""
|
|
45
|
+
result = subprocess.run([bash, "-c", mocks + block + "\necho stop-complete\n"],
|
|
46
|
+
capture_output=True, text=True)
|
|
47
|
+
assert result.returncode == expected, result.stdout + result.stderr
|
|
48
|
+
assert ("stop-complete" in result.stdout) == (expected == 0)
|
|
49
|
+
|
|
50
|
+
|
|
19
51
|
def workflow(filename):
|
|
20
52
|
# BaseLoader preserves GitHub's `on` key instead of YAML 1.1's boolean True.
|
|
21
53
|
return yaml.load((WORKFLOWS / filename).read_text(encoding="utf-8"), Loader=yaml.BaseLoader)
|
|
@@ -188,3 +220,38 @@ def test_shared_tools_trigger_only_consumers():
|
|
|
188
220
|
assert not accepts_changes(filename, "pull_request", ["tools/run_quality_gate.py"])
|
|
189
221
|
assert not accepts_changes("python-skill-csharp-package.yml", "pull_request", ["tools/run_import_smoke.py"])
|
|
190
222
|
assert not accepts_changes("xrefkit-skills-xddp-design.yml", "pull_request", ["xrefkit/cli.py"])
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_pages_deployment_is_main_only_and_release_dispatch_has_no_environment():
|
|
226
|
+
data = workflow("pages.yml")
|
|
227
|
+
deploy = data["jobs"]["deploy"]
|
|
228
|
+
condition = " ".join(deploy["if"].split())
|
|
229
|
+
assert condition.startswith("github.ref == 'refs/heads/main' && (")
|
|
230
|
+
assert deploy["environment"]["name"] == "github-pages"
|
|
231
|
+
route = data["jobs"]["dispatch-release"]
|
|
232
|
+
assert route["if"] == "github.event_name == 'release'"
|
|
233
|
+
assert "environment" not in route
|
|
234
|
+
assert route["permissions"] == {"contents": "read", "actions": "write"}
|
|
235
|
+
assert data["permissions"]["pages"] == "write"
|
|
236
|
+
step = route["steps"][0]
|
|
237
|
+
assert step["env"]["RELEASE_REPOSITORY"] == "${{ github.repository }}"
|
|
238
|
+
assert step["env"]["GH_TOKEN"] == "${{ github.token }}"
|
|
239
|
+
assert "github.event.release" not in step["run"]
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@pytest.mark.parametrize("dispatch_exit", [0, 37])
|
|
243
|
+
def test_pages_release_dispatch_executes_fixed_main_target_and_preserves_failure(dispatch_exit):
|
|
244
|
+
bash = shutil.which("bash")
|
|
245
|
+
if Path("C:/Program Files/Git/bin/bash.exe").is_file():
|
|
246
|
+
bash = "C:/Program Files/Git/bin/bash.exe"
|
|
247
|
+
if not bash:
|
|
248
|
+
pytest.skip("Bash is required to execute the Pages dispatch block")
|
|
249
|
+
script = workflow("pages.yml")["jobs"]["dispatch-release"]["steps"][0]["run"]
|
|
250
|
+
mock = ('set -euo pipefail\nRELEASE_REPOSITORY=synthaicode/XRefKit\n'
|
|
251
|
+
'gh() { printf "<%s>\\n" "$@"; return ' + str(dispatch_exit) + '; }\n')
|
|
252
|
+
result = subprocess.run([bash, "-c", mock + script + "\necho dispatch-complete\n"],
|
|
253
|
+
capture_output=True, text=True)
|
|
254
|
+
assert result.returncode == dispatch_exit, result.stdout + result.stderr
|
|
255
|
+
assert result.stdout.splitlines()[:6] == ["<api>", "<--method>", "<POST>",
|
|
256
|
+
"<repos/synthaicode/XRefKit/actions/workflows/pages.yml/dispatches>", "<-f>", "<ref=main>"]
|
|
257
|
+
assert ("dispatch-complete" in result.stdout) == (dispatch_exit == 0)
|
|
@@ -1000,7 +1000,13 @@ class CliTests(unittest.TestCase):
|
|
|
1000
1000
|
)
|
|
1001
1001
|
text = out.read_text(encoding="utf-8")
|
|
1002
1002
|
self.assertIn("## Domain Knowledge Inputs", text)
|
|
1003
|
-
self.assertIn("-
|
|
1003
|
+
self.assertIn("- log_format: `compact_v1`", text)
|
|
1004
|
+
self.assertIn("- operating_contract_xid: `B7A2C94F0E61`", text)
|
|
1005
|
+
self.assertIn(
|
|
1006
|
+
"### Selected Knowledge Inputs\n\n- target_service_structure:\n"
|
|
1007
|
+
" - `LOCAL-SERVICE-MAP-001`\n - `LOCAL-DB-SCHEMA-001`",
|
|
1008
|
+
text,
|
|
1009
|
+
)
|
|
1004
1010
|
self.assertIn("- xid: `LOCAL-SERVICE-MAP-001`", text)
|
|
1005
1011
|
self.assertIn("content_hash: `sha256-service-map`", text)
|
|
1006
1012
|
self.assertIn("last_verified: `2026-07-04`", text)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from test_skill_definition_runtime import _open_run
|
|
4
|
+
from test_subagent_startup import command
|
|
5
|
+
from xrefkit.skillrun import _section_body
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize("legacy", [False, True])
|
|
9
|
+
def test_compact_and_legacy_logs_keep_evidence_through_closure(tmp_path, legacy):
|
|
10
|
+
_, log = _open_run(tmp_path)
|
|
11
|
+
text = log.read_text(encoding="utf-8")
|
|
12
|
+
marker = "- log_format: `compact_v1`\n- operating_contract_xid: `B7A2C94F0E61`\n"
|
|
13
|
+
assert marker in text
|
|
14
|
+
assert "- rule:" not in text
|
|
15
|
+
if legacy:
|
|
16
|
+
text = text.replace(marker, "", 1)
|
|
17
|
+
text = text.replace(
|
|
18
|
+
"## Token Usage\n\n", "## Token Usage\n\n- rule: legacy token guidance\n", 1
|
|
19
|
+
)
|
|
20
|
+
log.write_text(text, encoding="utf-8")
|
|
21
|
+
|
|
22
|
+
def update(*args):
|
|
23
|
+
code, output = command("skill", *args, "--log", str(log))
|
|
24
|
+
assert code == 0, output
|
|
25
|
+
|
|
26
|
+
# User data that resembles omitted template lines must survive updates.
|
|
27
|
+
note = "literal evidence\n- rule: user supplied detail\n- log_format: `compact_v1`"
|
|
28
|
+
update("tokens", "--input", "120", "--output", "30", "--note", note)
|
|
29
|
+
update("workitem", "--item", "WI-1", "--completion-criterion", "report covers target", "--status", "done", "--role", "definition_sample:executor")
|
|
30
|
+
for artifact_id, kind, target in (
|
|
31
|
+
("OUT", "output", "report.md"), ("EVIDENCE", "evidence", "tests passed"),
|
|
32
|
+
):
|
|
33
|
+
update("artifact", "--artifact", artifact_id, "--kind", kind, "--target", target,
|
|
34
|
+
"--item", "WI-1", "--status", "done", "--role", "definition_sample:executor")
|
|
35
|
+
update("concern", "--concern", "RISK", "--kind", "risk", "--status", "resolved",
|
|
36
|
+
"--text", "Compatibility checked", "--target", "tests passed", "--role", "definition_sample:executor")
|
|
37
|
+
for phase in ("startup", "planning", "execution"):
|
|
38
|
+
update("phase", "--phase", phase, "--status", "done", "--role", "definition_sample:executor")
|
|
39
|
+
update("verify")
|
|
40
|
+
update("phase", "--phase", "handoff", "--status", "done", "--role", "definition_sample:handoff_owner")
|
|
41
|
+
update("close")
|
|
42
|
+
text = log.read_text(encoding="utf-8")
|
|
43
|
+
assert note in text
|
|
44
|
+
assert "Compatibility checked" in text
|
|
45
|
+
assert ("- rule: each work item requires" in text) is legacy
|
|
46
|
+
assert ("- rule: outputs, evidence" in text) is legacy
|
|
47
|
+
assert ("- rule: unknowns, missing evidence" in text) is legacy
|
|
48
|
+
assert "- total: `150`" in text
|
|
49
|
+
assert "- target: report.md" in text or "target=`report.md`" in text
|
|
50
|
+
assert "## Closure Gate\n\n- status: `done`" in text
|
|
51
|
+
assert ("- rule: record tokens consumed" in text) is legacy
|
|
52
|
+
for section in ("Worklist", "Quality Gate"):
|
|
53
|
+
body, _, _ = _section_body(text, section)
|
|
54
|
+
for heading in ("Status", "Result", "Checks Performed", "Evidence", "Open Items", "Handoff"):
|
|
55
|
+
assert f"### {heading}\n" in body
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_workflow_log_keeps_existing_format(tmp_path):
|
|
59
|
+
log = tmp_path / "work" / "workflow.md"
|
|
60
|
+
code, output = command(
|
|
61
|
+
"workflow", "run", "--root", str(tmp_path), "--task", "Inspect target",
|
|
62
|
+
"--completion-condition", "Target inspected", "--out", str(log),
|
|
63
|
+
)
|
|
64
|
+
assert code == 0, output
|
|
65
|
+
original = log.read_text(encoding="utf-8")
|
|
66
|
+
assert "log_format" not in original
|
|
67
|
+
assert "- rule: do not treat the instruction" in original
|
|
68
|
+
code, output = command("skill", "tokens", "--log", str(log), "--total", "20")
|
|
69
|
+
assert code == 0, output
|
|
70
|
+
assert "- rule: record tokens consumed" in log.read_text(encoding="utf-8")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_compaction_only_removes_template_rules_and_preserves_task(monkeypatch, tmp_path):
|
|
74
|
+
from xrefkit import skillrun
|
|
75
|
+
|
|
76
|
+
render = skillrun._render_log
|
|
77
|
+
captured = {}
|
|
78
|
+
|
|
79
|
+
def capture(**kwargs):
|
|
80
|
+
captured.update(kwargs)
|
|
81
|
+
return render(**kwargs)
|
|
82
|
+
|
|
83
|
+
monkeypatch.setattr(skillrun, "_render_log", capture)
|
|
84
|
+
_open_run(tmp_path)
|
|
85
|
+
captured["task"] = "Keep these literal lines:\n- rule: task requirement\n- log_format: `other`"
|
|
86
|
+
compact = render(**captured)
|
|
87
|
+
verbose = render(**{**captured, "compact": False})
|
|
88
|
+
assert captured["task"] in compact
|
|
89
|
+
assert len(compact.encode("utf-8")) < len(verbose.encode("utf-8")) * 0.8
|
|
90
|
+
|
|
91
|
+
# Every non-boilerplate line survives in the same order, including reports.
|
|
92
|
+
def data_lines(text):
|
|
93
|
+
return [line for line in text.splitlines() if line and not line.startswith((
|
|
94
|
+
"- rule:", "- log_format:", "- operating_contract_xid:",
|
|
95
|
+
))]
|
|
96
|
+
|
|
97
|
+
assert data_lines(compact) == data_lines(verbose)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_dashboard_payload_is_identical_for_compact_and_verbose_runs(monkeypatch, tmp_path):
|
|
101
|
+
import json
|
|
102
|
+
import os
|
|
103
|
+
from datetime import datetime, timezone
|
|
104
|
+
from itertools import count
|
|
105
|
+
from uuid import UUID
|
|
106
|
+
|
|
107
|
+
from xrefkit import dashboard, skillrun
|
|
108
|
+
|
|
109
|
+
render = skillrun._render_log
|
|
110
|
+
captured = {}
|
|
111
|
+
|
|
112
|
+
def capture(**kwargs):
|
|
113
|
+
captured.update(kwargs)
|
|
114
|
+
return render(**kwargs)
|
|
115
|
+
|
|
116
|
+
monkeypatch.setattr(skillrun, "_render_log", capture)
|
|
117
|
+
_, log = _open_run(tmp_path)
|
|
118
|
+
provenance = {"capability": {"origin": "explicit_input", "selected_value": "repository inspection"}}
|
|
119
|
+
|
|
120
|
+
class FixedDatetime(datetime):
|
|
121
|
+
@classmethod
|
|
122
|
+
def now(cls, tz=None):
|
|
123
|
+
fixed = cls(2026, 10, 25, 12, 0, tzinfo=timezone.utc)
|
|
124
|
+
return fixed.astimezone(tz) if tz else fixed.replace(tzinfo=None)
|
|
125
|
+
|
|
126
|
+
monkeypatch.setattr(skillrun, "datetime", FixedDatetime)
|
|
127
|
+
monkeypatch.setattr(dashboard, "datetime", FixedDatetime)
|
|
128
|
+
snapshots = []
|
|
129
|
+
for compact in (False, True):
|
|
130
|
+
event_ids = count(1)
|
|
131
|
+
monkeypatch.setattr(skillrun.uuid, "uuid4", lambda: UUID(int=next(event_ids)))
|
|
132
|
+
identity = {**captured["definition_identity"], "runtime_provenance": json.dumps(
|
|
133
|
+
provenance, sort_keys=True, separators=(",", ":") if compact else None,
|
|
134
|
+
)}
|
|
135
|
+
log.write_text(render(**{**captured, "definition_identity": identity, "compact": compact}), encoding="utf-8")
|
|
136
|
+
|
|
137
|
+
def snapshot():
|
|
138
|
+
# Compare the whole public payload, with no fields removed. The same
|
|
139
|
+
# file path and frozen clocks make file/observation timestamps equal.
|
|
140
|
+
os.utime(log, (1792929600, 1792929600))
|
|
141
|
+
return dashboard.build_payload(tmp_path, log.parent)
|
|
142
|
+
|
|
143
|
+
def update(*args):
|
|
144
|
+
code, output = command("skill", *args, "--log", str(log))
|
|
145
|
+
assert code == 0, output
|
|
146
|
+
|
|
147
|
+
initial = snapshot()
|
|
148
|
+
assert "token_usage" in {item["code"] for item in initial["runs"][0]["missing_information"]}
|
|
149
|
+
update("workitem", "--item", "WI-1", "--text", "Inspect target",
|
|
150
|
+
"--completion-criterion", "report covers target", "--status", "done", "--role", "definition_sample:executor")
|
|
151
|
+
for artifact_id, kind, target in (("OUT-1", "output", "report.md"), ("EVD-1", "evidence", "tests passed")):
|
|
152
|
+
update("artifact", "--artifact", artifact_id, "--kind", kind, "--target", target,
|
|
153
|
+
"--item", "WI-1", "--status", "done", "--role", "definition_sample:executor")
|
|
154
|
+
update("concern", "--concern", "RISK-1", "--kind", "risk", "--status", "resolved",
|
|
155
|
+
"--text", "Compatibility checked", "--target", "tests passed", "--role", "definition_sample:executor")
|
|
156
|
+
update("correlate", "--run-id", captured["run_id"], "--mcp-session-id", "mcp-1", "--repository-fingerprint", "repo-1")
|
|
157
|
+
update("routing", "--selected-skill", "definition_sample", "--candidate", "definition_sample", "--reason", "best match")
|
|
158
|
+
update("knowledge", "--action", "search", "--query", "service map", "--status", "hit", "--xid", "SERVICE123456")
|
|
159
|
+
update("knowledge", "--action", "load", "--xid", "SERVICE123456", "--content-hash", "hash-1")
|
|
160
|
+
update("knowledge", "--action", "apply", "--xid", "SERVICE123456", "--content-hash", "hash-1", "--target", "OUT-1")
|
|
161
|
+
update("feedback", "--kind", "human", "--status", "accepted", "--note", "accepted")
|
|
162
|
+
update("feedback", "--kind", "outcome", "--status", "successful", "--note", "successful")
|
|
163
|
+
update("tokens", "--input", "120", "--output", "30")
|
|
164
|
+
populated = snapshot()
|
|
165
|
+
run = populated["runs"][0]
|
|
166
|
+
assert run["missing_information"] == []
|
|
167
|
+
assert run["counts"]["work_items"] == 1
|
|
168
|
+
assert run["counts"]["artifacts"] == 2
|
|
169
|
+
assert run["counts"]["risks"] == 1
|
|
170
|
+
assert run["loaded_xids"] == run["used_xids"] == ["SERVICE123456"]
|
|
171
|
+
assert run["definition_format"] == "skill_definition_v1"
|
|
172
|
+
assert run["definition_xid"] == "ABCDEF123456"
|
|
173
|
+
assert run["definition_sha256"] == captured["definition_identity"]["sha256"]
|
|
174
|
+
assert run["capability"] == "repository inspection"
|
|
175
|
+
assert {event["event"] for event in run["observation_events"]} >= {
|
|
176
|
+
"skill.routed", "knowledge.search", "knowledge.loaded", "knowledge.applied", "human.feedback", "outcome.feedback",
|
|
177
|
+
}
|
|
178
|
+
for phase in ("startup", "planning", "execution"):
|
|
179
|
+
update("phase", "--phase", phase, "--status", "done", "--role", "definition_sample:executor")
|
|
180
|
+
update("verify")
|
|
181
|
+
update("phase", "--phase", "handoff", "--status", "done", "--role", "definition_sample:handoff_owner")
|
|
182
|
+
update("close")
|
|
183
|
+
closed = snapshot()
|
|
184
|
+
assert closed["runs"][0]["status"] == "closed"
|
|
185
|
+
text = log.read_text(encoding="utf-8")
|
|
186
|
+
assert json.loads(skillrun._log_field(text, "definition_runtime_provenance")) == provenance
|
|
187
|
+
assert skillrun._parse_token_usage(text) == {"status": "recorded", "input": "120", "output": "30", "total": "150"}
|
|
188
|
+
snapshots.append((initial, populated, closed))
|
|
189
|
+
|
|
190
|
+
assert snapshots[0] == snapshots[1]
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Raw-byte preparation checks without human review, gate dispatch or adoption."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
import uuid
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from test_subagent_startup import command
|
|
12
|
+
from xrefkit import governance_entry
|
|
13
|
+
from xrefkit.mcp import contribution_returns as cr
|
|
14
|
+
from xrefkit.mcp.audit import SessionRunBinding
|
|
15
|
+
from xrefkit.mcp.catalog import XRefCatalog
|
|
16
|
+
from xrefkit.mcp.update_gate_intake import prepare_contribution_gate
|
|
17
|
+
from xrefkit.ownership import Ownership, Zone
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
VARIANTS = [
|
|
21
|
+
pytest.param(b"# Rule\nalpha\nbeta\n", id="LF"),
|
|
22
|
+
pytest.param(b"# Rule\r\nalpha\r\nbeta\r\n", id="CRLF"),
|
|
23
|
+
pytest.param(b"# Rule\ralpha\rbeta\r", id="CR"),
|
|
24
|
+
pytest.param(b"# Rule\r\nalpha\nbeta\rgamma\r\n", id="mixed"),
|
|
25
|
+
pytest.param("# 検証\n条件:日本語\n".encode(), id="nonASCII-LF"),
|
|
26
|
+
pytest.param("# 検証\r\n条件:日本語\r\n".encode(), id="nonASCII-CRLF"),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _pending(root: Path, raw: bytes) -> tuple[Path, dict]:
|
|
31
|
+
content = "<!-- xid: BUG3TEST01 -->\n" + raw.decode("utf-8", errors="strict")
|
|
32
|
+
identity = str(uuid.uuid4())
|
|
33
|
+
cr.submit_contribution_return(
|
|
34
|
+
root,
|
|
35
|
+
binding=SessionRunBinding(str(uuid.uuid4()), str(uuid.uuid4()), "a" * 32, "sample"),
|
|
36
|
+
source_snapshot={"provider_id": "xrefkit-mcp", "provider_version": "test",
|
|
37
|
+
"package_id": None, "package_version": None, "skill_id": "sample",
|
|
38
|
+
"skill_content_hash": "b" * 64, "knowledge_versions": [],
|
|
39
|
+
"verification": "matched_current_catalog"},
|
|
40
|
+
contribution_id=identity, kind="knowledge", title="Byte fixture",
|
|
41
|
+
summary="Unapproved preparation fixture", files=[{"path": "rule.md", "content": content,
|
|
42
|
+
"content_hash": hashlib.sha256(content.encode()).hexdigest()}],
|
|
43
|
+
knowledge={"xid": "BUG3TEST01"}, proposed_target_path="knowledge/byte_fixture.md",
|
|
44
|
+
)
|
|
45
|
+
record = root / cr.STORE_RELATIVE / identity
|
|
46
|
+
return record, json.loads((record / "manifest.json").read_bytes())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _inventory(root: Path) -> dict[str, bytes]:
|
|
50
|
+
return {p.relative_to(root).as_posix(): p.read_bytes()
|
|
51
|
+
for p in root.rglob("*") if p.is_file()}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.parametrize("raw", VARIANTS)
|
|
55
|
+
def test_stored_files_preserve_original_bytes_and_repeated_reads(tmp_path: Path, raw: bytes) -> None:
|
|
56
|
+
record, manifest = _pending(tmp_path, raw)
|
|
57
|
+
original = (record / "files/rule.md").read_bytes()
|
|
58
|
+
before = _inventory(tmp_path)
|
|
59
|
+
assert cr.list_contribution_returns(tmp_path)[0]["status"] == "pending_review"
|
|
60
|
+
exported = cr.export_contribution_return(tmp_path, manifest["contribution_id"])
|
|
61
|
+
assert exported["review_bundle"]["files"][0]["content"].encode() == original
|
|
62
|
+
cr._verify_manifest_payload(record, manifest)
|
|
63
|
+
first = cr._stored_files(record, manifest)
|
|
64
|
+
assert first == cr._stored_files(record, manifest)
|
|
65
|
+
assert first[0].content.encode("utf-8") == original
|
|
66
|
+
assert first[0].content_hash == hashlib.sha256(original).hexdigest()
|
|
67
|
+
assert manifest["files"][0]["byte_count"] == len(original)
|
|
68
|
+
# This is the same encode used by gate intake, without bypassing its approval gate.
|
|
69
|
+
staged = tmp_path / "scratch/unapproved-candidate.md"
|
|
70
|
+
staged.parent.mkdir()
|
|
71
|
+
staged.write_bytes(first[0].content.encode("utf-8"))
|
|
72
|
+
assert staged.read_bytes() == original
|
|
73
|
+
staged.unlink()
|
|
74
|
+
assert _inventory(tmp_path) == before
|
|
75
|
+
assert not (record / "events").exists()
|
|
76
|
+
assert not (tmp_path / "knowledge").exists()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@pytest.mark.parametrize("corruption", ["hash", "byte_count", "invalid_utf8", "changed_bytes", "missing", "newline_only"])
|
|
80
|
+
def test_stored_files_reject_corrupt_candidate(tmp_path: Path, corruption: str) -> None:
|
|
81
|
+
record, manifest = _pending(tmp_path, b"# Rule\nbody\n")
|
|
82
|
+
stored = record / "files/rule.md"
|
|
83
|
+
if corruption == "hash":
|
|
84
|
+
manifest["files"][0]["content_hash"] = "0" * 64
|
|
85
|
+
elif corruption == "byte_count":
|
|
86
|
+
manifest["files"][0]["byte_count"] += 1
|
|
87
|
+
elif corruption == "invalid_utf8":
|
|
88
|
+
invalid = stored.read_bytes() + b"\xff"
|
|
89
|
+
stored.write_bytes(invalid)
|
|
90
|
+
manifest["files"][0].update(content_hash=hashlib.sha256(invalid).hexdigest(), byte_count=len(invalid))
|
|
91
|
+
elif corruption == "changed_bytes":
|
|
92
|
+
stored.write_bytes(stored.read_bytes() + b"tamper")
|
|
93
|
+
elif corruption == "newline_only":
|
|
94
|
+
stored.write_bytes(stored.read_bytes().replace(b"\n", b"\r\n"))
|
|
95
|
+
else:
|
|
96
|
+
stored.unlink()
|
|
97
|
+
before = _inventory(tmp_path)
|
|
98
|
+
with pytest.raises((ValueError, FileNotFoundError)):
|
|
99
|
+
cr._stored_files(record, manifest)
|
|
100
|
+
assert _inventory(tmp_path) == before
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.parametrize("corruption", ["payload", "extra_file", "deleted_file", "byte_count", "metadata_xid_tamper"])
|
|
104
|
+
def test_preparation_verifier_retains_manifest_and_tree_rejections(tmp_path: Path, corruption: str) -> None:
|
|
105
|
+
record, manifest = _pending(tmp_path, b"# Rule\nbody\n")
|
|
106
|
+
if corruption == "payload":
|
|
107
|
+
manifest["summary"] += " tampered"
|
|
108
|
+
elif corruption == "extra_file":
|
|
109
|
+
(record / "files/extra.md").write_bytes(b"extra")
|
|
110
|
+
elif corruption == "deleted_file":
|
|
111
|
+
(record / "files/rule.md").unlink()
|
|
112
|
+
elif corruption == "byte_count":
|
|
113
|
+
manifest["files"][0]["byte_count"] += 1
|
|
114
|
+
else:
|
|
115
|
+
manifest["kind_metadata"]["xid"] = "UNLISTED01"
|
|
116
|
+
before = _inventory(tmp_path)
|
|
117
|
+
with pytest.raises(ValueError):
|
|
118
|
+
cr._verify_manifest_payload(record, manifest)
|
|
119
|
+
assert _inventory(tmp_path) == before
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class _ReadOnlyTargetRoot:
|
|
123
|
+
"""Route a logical target to a real scratch file; never create canonical paths."""
|
|
124
|
+
def __init__(self, target: Path):
|
|
125
|
+
self.target = target
|
|
126
|
+
|
|
127
|
+
def joinpath(self, *parts: str) -> Path:
|
|
128
|
+
assert parts == ("knowledge", "byte_fixture.md")
|
|
129
|
+
return self.target
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _target_check(target: Path, raw: bytes, **changes) -> str:
|
|
133
|
+
xid = "BUG3TEST01"
|
|
134
|
+
logical = "knowledge/byte_fixture.md"
|
|
135
|
+
kwargs = dict(ownership=Ownership((Zone("kernel-content", "base", ("knowledge/",), True,
|
|
136
|
+
True, True, False),)),
|
|
137
|
+
existing_knowledge_xids={xid}, existing_knowledge_xid_locations={xid: {logical}},
|
|
138
|
+
repository_xid_paths={xid: {logical}}, allow_same_xid_after_prepare=True)
|
|
139
|
+
kwargs.update(changes)
|
|
140
|
+
return cr._validate_canonical_target(
|
|
141
|
+
_ReadOnlyTargetRoot(target),
|
|
142
|
+
{"kind": "knowledge", "kind_metadata": {"xid": xid},
|
|
143
|
+
"files": [{"content_hash": hashlib.sha256(raw).hexdigest(), "byte_count": len(raw)}]},
|
|
144
|
+
logical, **kwargs,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@pytest.mark.parametrize("raw", VARIANTS)
|
|
149
|
+
def test_recovery_target_matches_raw_hash_without_newline_conversion(tmp_path: Path, raw: bytes) -> None:
|
|
150
|
+
target = tmp_path / "scratch-target.md"
|
|
151
|
+
target.write_bytes(raw)
|
|
152
|
+
before = target.read_bytes()
|
|
153
|
+
assert _target_check(target, raw) == "knowledge/byte_fixture.md"
|
|
154
|
+
assert target.read_bytes() == before
|
|
155
|
+
assert not (tmp_path / "knowledge").exists()
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pytest.mark.parametrize("mismatch", ["target_version", "expected_hash", "duplicate_xid", "unlocated_xid", "no_prepare", "missing_target", "newline_only"])
|
|
159
|
+
def test_recovery_retains_collision_and_version_rejections(tmp_path: Path, mismatch: str) -> None:
|
|
160
|
+
original = b"# Original\nbody\n"
|
|
161
|
+
target = tmp_path / "scratch-target.md"
|
|
162
|
+
target.write_bytes(original)
|
|
163
|
+
expected = original
|
|
164
|
+
changes = {}
|
|
165
|
+
if mismatch == "target_version":
|
|
166
|
+
target.write_bytes(b"# Other version\nbody\n")
|
|
167
|
+
elif mismatch == "newline_only":
|
|
168
|
+
target.write_bytes(original.replace(b"\n", b"\r\n"))
|
|
169
|
+
elif mismatch == "expected_hash":
|
|
170
|
+
expected = b"# Different expected base\nbody\n"
|
|
171
|
+
elif mismatch == "duplicate_xid":
|
|
172
|
+
changes["repository_xid_paths"] = {"BUG3TEST01": {"knowledge/byte_fixture.md", "knowledge/other.md"}}
|
|
173
|
+
elif mismatch == "unlocated_xid":
|
|
174
|
+
changes["unlocated_existing_knowledge_xids"] = {"BUG3TEST01"}
|
|
175
|
+
elif mismatch == "no_prepare":
|
|
176
|
+
changes["allow_same_xid_after_prepare"] = False
|
|
177
|
+
else:
|
|
178
|
+
target.unlink()
|
|
179
|
+
before = _inventory(tmp_path)
|
|
180
|
+
with pytest.raises(ValueError, match="Knowledge XID already exists"):
|
|
181
|
+
_target_check(target, expected, **changes)
|
|
182
|
+
assert _inventory(tmp_path) == before
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_gate_preparation_requires_real_approval_and_leaves_pending_state(tmp_path: Path) -> None:
|
|
186
|
+
record, manifest = _pending(tmp_path, b"# Rule\r\nbody\r\n")
|
|
187
|
+
before = _inventory(tmp_path)
|
|
188
|
+
with pytest.raises(RuntimeError, match="trusted human approval verifier"):
|
|
189
|
+
prepare_contribution_gate(tmp_path, contribution_id=manifest["contribution_id"],
|
|
190
|
+
log="work/not-created.md", binding_request={},
|
|
191
|
+
specialist_evidence=[], approval_verifier=None)
|
|
192
|
+
assert _inventory(tmp_path) == before
|
|
193
|
+
assert not (record / "events").exists()
|
|
194
|
+
assert not (tmp_path / "knowledge").exists()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_unsigned_gate_packet_revalidation_rejects_wrong_base_hash(tmp_path: Path) -> None:
|
|
198
|
+
log = tmp_path / "work/run.md"
|
|
199
|
+
code, output = command("workflow", "run", "--root", str(tmp_path), "--task",
|
|
200
|
+
"Unsigned base hash regression fixture", "--out", str(log),
|
|
201
|
+
"--completion-condition", "read-only packet checked", "--json")
|
|
202
|
+
assert code == 0, output
|
|
203
|
+
code, output = command("skill", "workitem", "--log", str(log), "--item", "WI-BASE",
|
|
204
|
+
"--text", "Validate unsigned frozen packet", "--completion-criterion",
|
|
205
|
+
"base mismatch rejected without writing target", "--status", "pending",
|
|
206
|
+
"--role", "instruction:executor", "--json")
|
|
207
|
+
assert code == 0, output
|
|
208
|
+
paths = ["work/candidate.md", *sorted(governance_entry.RULE_SOURCES)]
|
|
209
|
+
for name in paths:
|
|
210
|
+
path = tmp_path / name
|
|
211
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
212
|
+
path.write_bytes(b"# Isolated unsigned packet material\r\n")
|
|
213
|
+
request = {"work_item_id": "WI-BASE", "source_mode": "filesystem", "purpose": "base hash test",
|
|
214
|
+
"capability": "analysis", "tuning": "read-only fixture", "responsibility": "reject changed base",
|
|
215
|
+
"instruction_basis": "Authorized BUG3 regression scope", "scope_in": paths,
|
|
216
|
+
"scope_out": ["approval", "dispatch", "adoption"], "stop_conditions": ["unexpected write"],
|
|
217
|
+
"protocols": ["workflow"], "knowledge_access": {"mode": "on_demand", "catalog": "work/index.md"}}
|
|
218
|
+
packet = governance_entry.prepare(
|
|
219
|
+
tmp_path, kind="asset_update", log="work/run.md", binding_request=request, materials=paths,
|
|
220
|
+
payload={"candidate": "work/candidate.md", "target": "knowledge/not-created.md",
|
|
221
|
+
"rules": sorted(governance_entry.RULE_SOURCES), "checks": sorted(governance_entry.CORE_CHECKS),
|
|
222
|
+
"specialist_evidence": []},
|
|
223
|
+
)
|
|
224
|
+
assert packet["payload"]["expected_target_hash"] is None
|
|
225
|
+
before = _inventory(tmp_path)
|
|
226
|
+
governance_entry.revalidate(tmp_path, packet)
|
|
227
|
+
packet["payload"]["expected_target_hash"] = "0" * 64
|
|
228
|
+
# Packet digest is an unsigned content digest, never human authority or a host receipt.
|
|
229
|
+
packet["packet_hash"] = governance_entry.digest({k: v for k, v in packet.items() if k != "packet_hash"})
|
|
230
|
+
with pytest.raises(ValueError, match="target baseline changed"):
|
|
231
|
+
governance_entry.revalidate(tmp_path, packet)
|
|
232
|
+
assert _inventory(tmp_path) == before
|
|
233
|
+
assert not (tmp_path / "knowledge").exists()
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_nonexistent_xid_lookup_does_not_invent_a_document(tmp_path: Path) -> None:
|
|
237
|
+
before = _inventory(tmp_path)
|
|
238
|
+
with pytest.raises(KeyError, match="document xid not found: ABCDEF987654"):
|
|
239
|
+
XRefCatalog.build(tmp_path).get_document_by_xid("ABCDEF987654")
|
|
240
|
+
assert _inventory(tmp_path) == before
|