xrefkit 0.4.16__tar.gz → 0.5.1__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.4.16/xrefkit.egg-info → xrefkit-0.5.1}/PKG-INFO +60 -12
- xrefkit-0.5.1/README.md +130 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/pyproject.toml +1 -1
- xrefkit-0.5.1/tests/test_artifact_selected_family.py +46 -0
- xrefkit-0.5.1/tests/test_attention_pet.py +257 -0
- xrefkit-0.5.1/tests/test_attention_pet_client_protocol.py +207 -0
- xrefkit-0.5.1/tests/test_attention_pet_codex_session.py +100 -0
- xrefkit-0.5.1/tests/test_attention_pet_fit.py +339 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_boundary_analysis.py +42 -1
- xrefkit-0.5.1/tests/test_brownfield_csharp_structure_definitions.py +44 -0
- xrefkit-0.5.1/tests/test_business_intake_conversation_definitions.py +46 -0
- xrefkit-0.5.1/tests/test_business_intake_definition.py +55 -0
- xrefkit-0.5.1/tests/test_catalog_preparation_definition.py +47 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_cli.py +9 -1
- xrefkit-0.5.1/tests/test_code_constraint_definition.py +47 -0
- xrefkit-0.5.1/tests/test_constraint_derivation_family_definition.py +68 -0
- xrefkit-0.5.1/tests/test_constraint_derivation_selected_family.py +44 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_contribution_returns.py +1 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_dashboard.py +68 -0
- xrefkit-0.5.1/tests/test_database_batch_definition_migration.py +90 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_decision_trace.py +28 -0
- xrefkit-0.5.1/tests/test_design_business_intake_selected_family.py +36 -0
- xrefkit-0.5.1/tests/test_design_planning_definition_migration.py +72 -0
- xrefkit-0.5.1/tests/test_dotnet_definition_candidate.py +103 -0
- xrefkit-0.5.1/tests/test_editorial_family_definition.py +58 -0
- xrefkit-0.5.1/tests/test_editorial_intake_definition.py +28 -0
- xrefkit-0.5.1/tests/test_editorial_selected_family.py +42 -0
- xrefkit-0.5.1/tests/test_embedded_startup_pack.py +45 -0
- xrefkit-0.5.1/tests/test_execution_binding.py +198 -0
- xrefkit-0.5.1/tests/test_existing_skill_definition_batch.py +27 -0
- xrefkit-0.5.1/tests/test_final_skilldefinition_batch.py +54 -0
- xrefkit-0.5.1/tests/test_flow_selected_family.py +44 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_gateway.py +45 -0
- xrefkit-0.5.1/tests/test_gateway_mcp.py +236 -0
- xrefkit-0.5.1/tests/test_gateway_skill_adapter.py +342 -0
- xrefkit-0.5.1/tests/test_gateway_work_items.py +542 -0
- xrefkit-0.5.1/tests/test_implementation_flow_definition.py +75 -0
- xrefkit-0.5.1/tests/test_implementation_review_selected_family.py +36 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_instruction_workflow.py +5 -0
- xrefkit-0.5.1/tests/test_language_review_definition.py +58 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_mcp_package_routing.py +79 -0
- xrefkit-0.5.1/tests/test_mcp_subagent_startup.py +452 -0
- xrefkit-0.5.1/tests/test_mcp_subagent_startup_integration.py +170 -0
- xrefkit-0.5.1/tests/test_os_family_definition.py +65 -0
- xrefkit-0.5.1/tests/test_os_knowledge_family_definition.py +54 -0
- xrefkit-0.5.1/tests/test_os_selected_family.py +45 -0
- xrefkit-0.5.1/tests/test_qa_report_definition.py +51 -0
- xrefkit-0.5.1/tests/test_security_definition_migration.py +37 -0
- xrefkit-0.5.1/tests/test_skill_definition.py +157 -0
- xrefkit-0.5.1/tests/test_skill_definition_governance.py +121 -0
- xrefkit-0.5.1/tests/test_skill_definition_mcp.py +262 -0
- xrefkit-0.5.1/tests/test_skill_definition_runtime.py +193 -0
- xrefkit-0.5.1/tests/test_subagent_startup.py +154 -0
- xrefkit-0.5.1/tests/test_subagent_startup_boundaries.py +153 -0
- xrefkit-0.5.1/tests/test_workflow_family_definition.py +60 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_xrefkit_v2_pipeline.py +15 -6
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/__init__.py +1 -1
- xrefkit-0.5.1/xrefkit/attention_pet/__init__.py +1 -0
- xrefkit-0.5.1/xrefkit/attention_pet/__main__.py +78 -0
- xrefkit-0.5.1/xrefkit/attention_pet/client.py +80 -0
- xrefkit-0.5.1/xrefkit/attention_pet/client_protocol.py +136 -0
- xrefkit-0.5.1/xrefkit/attention_pet/codex_session.py +151 -0
- xrefkit-0.5.1/xrefkit/attention_pet/evaluator.py +222 -0
- xrefkit-0.5.1/xrefkit/attention_pet/model.py +225 -0
- xrefkit-0.5.1/xrefkit/attention_pet/presentation.py +195 -0
- xrefkit-0.5.1/xrefkit/attention_pet/profiles.py +31 -0
- xrefkit-0.5.1/xrefkit/attention_pet/scenarios.py +42 -0
- xrefkit-0.5.1/xrefkit/attention_pet/server.py +144 -0
- xrefkit-0.5.1/xrefkit/attention_pet/store.py +118 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/boundary_analysis.py +111 -60
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/cli.py +5 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/dashboard.py +57 -0
- xrefkit-0.5.1/xrefkit/execution_binding.py +205 -0
- xrefkit-0.5.1/xrefkit/execution_binding_cli.py +20 -0
- xrefkit-0.5.1/xrefkit/gateway.py +1733 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/catalog.py +397 -32
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/cli.py +14 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/contracts.py +8 -0
- xrefkit-0.5.1/xrefkit/mcp/gateway.py +169 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/schemas.py +10 -6
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/server.py +183 -23
- xrefkit-0.5.1/xrefkit/mcp/startup_contract_pack.py +122 -0
- xrefkit-0.5.1/xrefkit/mcp/subagent_startup.py +316 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/operations_cli.py +55 -1
- xrefkit-0.5.1/xrefkit/resources/attention_pet/client-state.schema.json +306 -0
- xrefkit-0.5.1/xrefkit/resources/attention_pet/pet.css +97 -0
- xrefkit-0.5.1/xrefkit/resources/attention_pet/pet.html +42 -0
- xrefkit-0.5.1/xrefkit/resources/attention_pet/pet.js +207 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/startup_sources/011_startup_xref_routing.md +5 -3
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/startup_sources/017_base_and_xref_layering.md +3 -1
- xrefkit-0.5.1/xrefkit/skill_definition.py +260 -0
- xrefkit-0.5.1/xrefkit/skill_definition_catalog.py +91 -0
- xrefkit-0.5.1/xrefkit/skill_definition_governance.py +133 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/skillrun.py +105 -16
- xrefkit-0.5.1/xrefkit/subagent_startup.py +166 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1/xrefkit.egg-info}/PKG-INFO +60 -12
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit.egg-info/SOURCES.txt +66 -1
- xrefkit-0.4.16/README.md +0 -82
- xrefkit-0.4.16/tests/test_base_sync_ownership.py +0 -123
- xrefkit-0.4.16/tests/test_gateway_mcp.py +0 -116
- xrefkit-0.4.16/xrefkit/gateway.py +0 -450
- xrefkit-0.4.16/xrefkit/mcp/gateway.py +0 -98
- xrefkit-0.4.16/xrefkit/mcp/startup_contract_pack.py +0 -170
- {xrefkit-0.4.16 → xrefkit-0.5.1}/LICENSE +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/setup.cfg +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_brownfield_file_editing_protocol.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_calibration_lint.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_check_skill_knowledge_xids.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_collect_analyzer_sarif.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_convert_to_xrefkit_skill.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_cs_scope_probe.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_csharp_commonality.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_csharp_naming_profile.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_ctx.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_cutover_readiness.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_error_policy_audit.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_error_policy_locator.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_fm_multiroot.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_gate.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_goal_desired_state.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_host_precheck.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_human_evaluation.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_inbound_uploads.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_knowledge_relations_validator.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_mcp_setup.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_ownership.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_packmeta.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_project_quality_baseline.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_resource_provider.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_runtime_contracts.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_sarif_to_locator.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_skill_edits.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_skill_maturity_return.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_skill_runtime_audit.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_skillmeta.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_skills_sync.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_structure_catalog.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_wbs.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_xref.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_xrefkit_instance.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_xrefkit_tools.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_xrefkit_v2_discovery.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/tests/test_xrefkit_v2_models.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/__main__.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/catalog_cli.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/contracts.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/ctx.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/decision_trace.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/discovery.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/gate.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/goalstate.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/hashing.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/host_precheck.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/import_skill.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/instance.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/loaders.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/__init__.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/audit.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/bootstrap.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/client_cache.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/client_flow.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/context_registry.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/context_token.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/contribution_adoption.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/contribution_returns.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/dist.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/inbound_uploads.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/knowledge_edits.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/ownership.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/repository.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/setup.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/skill_edits.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp/skill_maturity.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/mcp_tools.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/__init__.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/common.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/effective_bundle.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/human_evaluation.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/local_manifest.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/package_manifest.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/run_log.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/server_config.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/models/skill_definition.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/ownership.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/packmeta.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/registry.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resolver.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resource_provider.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/contracts.json +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/current.json +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/model_body.md +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/startup_sources/000_agent_entry.md +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/startup_sources/015_shared_memory_operations.md +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/startup_sources/016_uncertainty_protocol.md +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/resources/base/startup_sources/053_context_direction_security_guard.md +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/runlog.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/skillmeta.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/skills_sync.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/structure_catalog.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/tools/__init__.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/tools/__main__.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/v2_cli.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/wbs.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/workspace.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit/xref.py +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit.egg-info/dependency_links.txt +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit.egg-info/entry_points.txt +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit.egg-info/requires.txt +0 -0
- {xrefkit-0.4.16 → xrefkit-0.5.1}/xrefkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xrefkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
|
|
5
5
|
Author: synthaicode
|
|
6
6
|
License: MIT License
|
|
@@ -42,11 +42,44 @@ Dynamic: license-file
|
|
|
42
42
|
XRefKit is a framework for making AI-assisted work repeatable, reviewable, and
|
|
43
43
|
handoff-ready.
|
|
44
44
|
|
|
45
|
-
It helps teams
|
|
46
|
-
|
|
45
|
+
It helps teams structure domain procedures and knowledge so that work can
|
|
46
|
+
record evidence, preserve human judgment, and apply explicit completion checks.
|
|
47
47
|
|
|
48
48
|
## Why XRefKit?
|
|
49
49
|
|
|
50
|
+
Experimental local tool: [Attention Pet](projects/attention-pet/README.md) helps
|
|
51
|
+
people assess capability sufficiency and lower inference-cost candidates separately.
|
|
52
|
+
Its Model Fit / Cost Fit estimates use uncalibrated profiles; it does not measure internal
|
|
53
|
+
attention or switch models automatically.
|
|
54
|
+
|
|
55
|
+
### Codex-only preview: Attention Pet
|
|
56
|
+
|
|
57
|
+
Attention Pet is a small local companion for Codex work. It reads the current
|
|
58
|
+
Codex chat's local session record, estimates whether the selected model meets
|
|
59
|
+
the visible work requirements, and shows the result through a compact animated
|
|
60
|
+
pet. It also shows lower inference-cost comparison candidates when the
|
|
61
|
+
experimental profiles support one. The estimate is observational and does not
|
|
62
|
+
change the Codex model, reasoning level, or chat.
|
|
63
|
+
|
|
64
|
+
Run it from a Codex terminal in this repository so `CODEX_THREAD_ID` identifies
|
|
65
|
+
the current chat:
|
|
66
|
+
|
|
67
|
+
```powershell
|
|
68
|
+
python -m xrefkit.attention_pet serve --port 8769
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Open the printed `http://127.0.0.1:8769/` address in the Codex browser panel.
|
|
72
|
+
The display follows the browser's preferred language: Japanese is used for a
|
|
73
|
+
Japanese preference, with English as the fallback. Stop the process with
|
|
74
|
+
Ctrl+C.
|
|
75
|
+
|
|
76
|
+
This Codex preview is bound to the chat that launched it. Selecting another
|
|
77
|
+
chat does not move the Pet automatically; launch it again from that chat when
|
|
78
|
+
you want a separate view. The model and reasoning dropdowns only compare
|
|
79
|
+
estimates and do not change the active Codex settings. See the
|
|
80
|
+
[Attention Pet guide](projects/attention-pet/README.md) for interpretation,
|
|
81
|
+
limitations, client integration, and API details.
|
|
82
|
+
|
|
50
83
|
Using AI for real work creates recurring operating problems:
|
|
51
84
|
|
|
52
85
|

|
|
@@ -61,24 +94,34 @@ XRefKit provides a repository and runtime model for addressing these problems.
|
|
|
61
94
|
|
|
62
95
|
## What it provides
|
|
63
96
|
|
|
64
|
-
- **Skills** — reusable procedures
|
|
65
|
-
- **Knowledge** — source-backed domain facts and local rules
|
|
66
|
-
|
|
67
|
-
|
|
97
|
+
- **Skills** — reusable procedures expressed as SkillDefinitions
|
|
98
|
+
- **Knowledge** — source-backed domain facts and local rules resolved by XID
|
|
99
|
+
when a Skill run needs them
|
|
100
|
+
- **Workflow protocol** — recorded progress, deterministic workflow-record
|
|
101
|
+
verification, and closure checks
|
|
68
102
|
- **Evidence and handoffs** — outputs, judgments, concerns, and decisions that
|
|
69
103
|
remain reviewable after the work is done
|
|
70
|
-
- **Skill Run Dashboard** — inspect Skill execution
|
|
71
|
-
|
|
72
|
-
|
|
104
|
+
- **Skill Run Dashboard** — inspect recorded Skill execution state, definition
|
|
105
|
+
identity, runtime binding, referenced XIDs, evidence, judgments, concerns,
|
|
106
|
+
and handoffs
|
|
73
107
|
- **XIDs** — stable references for connecting procedures, knowledge, and
|
|
74
108
|
supporting documents
|
|
75
109
|
|
|
76
110
|
The package includes the resolver, Skill runtime, workflow controls, client
|
|
77
111
|
tools, and an optional MCP adapter.
|
|
78
112
|
|
|
113
|
+
Current Skill authoring targets the one-document `skill_definition_v1` format.
|
|
114
|
+
Existing `legacy_split_v1` Skills and unversioned historical run logs remain
|
|
115
|
+
readable during migration. The [Workflow Runtime Binding](docs/core/contracts/111_workflow_runtime_binding.md#xid-8D50A972BA9F)
|
|
116
|
+
defines how instruction-derived `capability`, `tuning`, `responsibility`, and
|
|
117
|
+
`execution_mode` are captured; these are not fixed SkillDefinition properties.
|
|
118
|
+
Knowledge used by a Skill remains separate and is resolved from the XID catalog
|
|
119
|
+
when needed.
|
|
120
|
+
|
|
79
121
|
The [Skill Run Dashboard](docs/guides/086_skill_run_observation_dashboard_usage.md#xid-4A4763A2DE63)
|
|
80
|
-
helps people
|
|
81
|
-
evidence before
|
|
122
|
+
helps people inspect recorded XID retrieval and use together with the run's
|
|
123
|
+
evidence before a human accepts or hands off the result. The Dashboard reports
|
|
124
|
+
recorded state and missing observations; it does not accept output quality.
|
|
82
125
|
|
|
83
126
|
## Quick start
|
|
84
127
|
|
|
@@ -104,12 +147,17 @@ python -m pip install "xrefkit[mcp]"
|
|
|
104
147
|
xrefkit mcp serve --repo . --transport stdio
|
|
105
148
|
```
|
|
106
149
|
|
|
150
|
+
This command starts the optional catalog and runtime MCP adapter. This checkout
|
|
151
|
+
does not implement management upload, staging, sealing, review, or adoption of
|
|
152
|
+
uploaded SkillDefinition bytes into the active catalog.
|
|
153
|
+
|
|
107
154
|
## Where to go next
|
|
108
155
|
|
|
109
156
|
- [Install XRefKit and register Skill Packages](docs/guides/089_xrefkit_package_first_registration.md#xid-4F8C2A7D1E90)
|
|
110
157
|
- [Understand the workflow protocol](docs/guides/087_workflow_protocol_sequence_for_humans.md#xid-E8B4D2F19A63)
|
|
111
158
|
- [Use an instruction-backed workflow](docs/guides/088_instruction_workflow_protocol.md#xid-9F4C2A7D1B60)
|
|
112
159
|
- [Understand Skills and Knowledge](docs/core/models/052_flow_capability_skill_knowledge_model.md#xid-91C4B7E2D5A8)
|
|
160
|
+
- [Read the SkillDefinition v1 contract](docs/core/contracts/096_skill_definition_contract.md#xid-E6A19D4B72C3)
|
|
113
161
|
- [Author a Skill with xref](docs/guides/013_skill_authoring_with_xref.md#xid-3DB05A0F5F5B)
|
|
114
162
|
- [Browse the complete documentation index](docs/000_index.md#xid-56DD6EB68343)
|
|
115
163
|
|
xrefkit-0.5.1/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# XRefKit
|
|
2
|
+
|
|
3
|
+
XRefKit is a framework for making AI-assisted work repeatable, reviewable, and
|
|
4
|
+
handoff-ready.
|
|
5
|
+
|
|
6
|
+
It helps teams structure domain procedures and knowledge so that work can
|
|
7
|
+
record evidence, preserve human judgment, and apply explicit completion checks.
|
|
8
|
+
|
|
9
|
+
## Why XRefKit?
|
|
10
|
+
|
|
11
|
+
Experimental local tool: [Attention Pet](projects/attention-pet/README.md) helps
|
|
12
|
+
people assess capability sufficiency and lower inference-cost candidates separately.
|
|
13
|
+
Its Model Fit / Cost Fit estimates use uncalibrated profiles; it does not measure internal
|
|
14
|
+
attention or switch models automatically.
|
|
15
|
+
|
|
16
|
+
### Codex-only preview: Attention Pet
|
|
17
|
+
|
|
18
|
+
Attention Pet is a small local companion for Codex work. It reads the current
|
|
19
|
+
Codex chat's local session record, estimates whether the selected model meets
|
|
20
|
+
the visible work requirements, and shows the result through a compact animated
|
|
21
|
+
pet. It also shows lower inference-cost comparison candidates when the
|
|
22
|
+
experimental profiles support one. The estimate is observational and does not
|
|
23
|
+
change the Codex model, reasoning level, or chat.
|
|
24
|
+
|
|
25
|
+
Run it from a Codex terminal in this repository so `CODEX_THREAD_ID` identifies
|
|
26
|
+
the current chat:
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
python -m xrefkit.attention_pet serve --port 8769
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Open the printed `http://127.0.0.1:8769/` address in the Codex browser panel.
|
|
33
|
+
The display follows the browser's preferred language: Japanese is used for a
|
|
34
|
+
Japanese preference, with English as the fallback. Stop the process with
|
|
35
|
+
Ctrl+C.
|
|
36
|
+
|
|
37
|
+
This Codex preview is bound to the chat that launched it. Selecting another
|
|
38
|
+
chat does not move the Pet automatically; launch it again from that chat when
|
|
39
|
+
you want a separate view. The model and reasoning dropdowns only compare
|
|
40
|
+
estimates and do not change the active Codex settings. See the
|
|
41
|
+
[Attention Pet guide](projects/attention-pet/README.md) for interpretation,
|
|
42
|
+
limitations, client integration, and API details.
|
|
43
|
+
|
|
44
|
+
Using AI for real work creates recurring operating problems:
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
- the AI can act from incomplete context or unsupported guesses
|
|
49
|
+
- procedures, domain facts, and judgment criteria get mixed together in prompts
|
|
50
|
+
- execution, checking, and handoff collapse into one opaque step
|
|
51
|
+
- work becomes hard to continue across agents, humans, or sessions
|
|
52
|
+
- outputs may lack evidence, closure discipline, or auditability
|
|
53
|
+
|
|
54
|
+
XRefKit provides a repository and runtime model for addressing these problems.
|
|
55
|
+
|
|
56
|
+
## What it provides
|
|
57
|
+
|
|
58
|
+
- **Skills** — reusable procedures expressed as SkillDefinitions
|
|
59
|
+
- **Knowledge** — source-backed domain facts and local rules resolved by XID
|
|
60
|
+
when a Skill run needs them
|
|
61
|
+
- **Workflow protocol** — recorded progress, deterministic workflow-record
|
|
62
|
+
verification, and closure checks
|
|
63
|
+
- **Evidence and handoffs** — outputs, judgments, concerns, and decisions that
|
|
64
|
+
remain reviewable after the work is done
|
|
65
|
+
- **Skill Run Dashboard** — inspect recorded Skill execution state, definition
|
|
66
|
+
identity, runtime binding, referenced XIDs, evidence, judgments, concerns,
|
|
67
|
+
and handoffs
|
|
68
|
+
- **XIDs** — stable references for connecting procedures, knowledge, and
|
|
69
|
+
supporting documents
|
|
70
|
+
|
|
71
|
+
The package includes the resolver, Skill runtime, workflow controls, client
|
|
72
|
+
tools, and an optional MCP adapter.
|
|
73
|
+
|
|
74
|
+
Current Skill authoring targets the one-document `skill_definition_v1` format.
|
|
75
|
+
Existing `legacy_split_v1` Skills and unversioned historical run logs remain
|
|
76
|
+
readable during migration. The [Workflow Runtime Binding](docs/core/contracts/111_workflow_runtime_binding.md#xid-8D50A972BA9F)
|
|
77
|
+
defines how instruction-derived `capability`, `tuning`, `responsibility`, and
|
|
78
|
+
`execution_mode` are captured; these are not fixed SkillDefinition properties.
|
|
79
|
+
Knowledge used by a Skill remains separate and is resolved from the XID catalog
|
|
80
|
+
when needed.
|
|
81
|
+
|
|
82
|
+
The [Skill Run Dashboard](docs/guides/086_skill_run_observation_dashboard_usage.md#xid-4A4763A2DE63)
|
|
83
|
+
helps people inspect recorded XID retrieval and use together with the run's
|
|
84
|
+
evidence before a human accepts or hands off the result. The Dashboard reports
|
|
85
|
+
recorded state and missing observations; it does not accept output quality.
|
|
86
|
+
|
|
87
|
+
## Quick start
|
|
88
|
+
|
|
89
|
+
XRefKit requires Python 3.11 or later.
|
|
90
|
+
|
|
91
|
+
```powershell
|
|
92
|
+
python -m pip install xrefkit
|
|
93
|
+
xrefkit init
|
|
94
|
+
xrefkit --help
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
For local development:
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
python -m pip install -e .
|
|
101
|
+
xrefkit init
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For the optional MCP server:
|
|
105
|
+
|
|
106
|
+
```powershell
|
|
107
|
+
python -m pip install "xrefkit[mcp]"
|
|
108
|
+
xrefkit mcp serve --repo . --transport stdio
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This command starts the optional catalog and runtime MCP adapter. This checkout
|
|
112
|
+
does not implement management upload, staging, sealing, review, or adoption of
|
|
113
|
+
uploaded SkillDefinition bytes into the active catalog.
|
|
114
|
+
|
|
115
|
+
## Where to go next
|
|
116
|
+
|
|
117
|
+
- [Install XRefKit and register Skill Packages](docs/guides/089_xrefkit_package_first_registration.md#xid-4F8C2A7D1E90)
|
|
118
|
+
- [Understand the workflow protocol](docs/guides/087_workflow_protocol_sequence_for_humans.md#xid-E8B4D2F19A63)
|
|
119
|
+
- [Use an instruction-backed workflow](docs/guides/088_instruction_workflow_protocol.md#xid-9F4C2A7D1B60)
|
|
120
|
+
- [Understand Skills and Knowledge](docs/core/models/052_flow_capability_skill_knowledge_model.md#xid-91C4B7E2D5A8)
|
|
121
|
+
- [Read the SkillDefinition v1 contract](docs/core/contracts/096_skill_definition_contract.md#xid-E6A19D4B72C3)
|
|
122
|
+
- [Author a Skill with xref](docs/guides/013_skill_authoring_with_xref.md#xid-3DB05A0F5F5B)
|
|
123
|
+
- [Browse the complete documentation index](docs/000_index.md#xid-56DD6EB68343)
|
|
124
|
+
|
|
125
|
+
## Security
|
|
126
|
+
|
|
127
|
+
XRefKit does not require provider API keys to explore or install the package.
|
|
128
|
+
Do not commit secrets, API keys, access tokens, `.env` files, or provider
|
|
129
|
+
credentials. Authenticate external AI tools through their official provider
|
|
130
|
+
mechanisms.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from xrefkit.mcp.catalog import XRefCatalog
|
|
6
|
+
from xrefkit.skill_definition import load_skill_definition
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
CASES = [
|
|
10
|
+
("xlsx_spec_traceability", "xlsx_spec_traceability", "A7C3E9D2F610", "2D1B6A9E7C40", "5A7C2D4E9130"),
|
|
11
|
+
("marketing-explainer-video", "marketing_explainer_video", "B8F1A6C4D720", "5E147B19D33D", "A6B923E41178"),
|
|
12
|
+
("marketing_slide_png", "marketing_slide_png", "C2E7B9F5A130", "91B67D4AC2F3", "5E2C4A90D711"),
|
|
13
|
+
("import_skill", "import_skill", "D5A8C1E6B740", "7C2A492D2B72", "1DF4555E1B02"),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.mark.parametrize("directory,skill_id,xid,body_xid,meta_xid", CASES)
|
|
18
|
+
def test_artifact_v1_parse_concrete_metadata_and_legacy(directory, skill_id, xid, body_xid, meta_xid):
|
|
19
|
+
repo = Path(__file__).resolve().parents[1]
|
|
20
|
+
relative = Path(f"skills/{directory}/SKILL.v1.md")
|
|
21
|
+
definition = load_skill_definition(repo / relative)
|
|
22
|
+
metadata = definition["metadata"]
|
|
23
|
+
assert metadata["skill_id"] == skill_id
|
|
24
|
+
assert metadata["xid"] == xid
|
|
25
|
+
assert set(metadata["aliases"]) == {body_xid, meta_xid}
|
|
26
|
+
assert metadata["inputs"] and metadata["outputs"]
|
|
27
|
+
assert metadata["control_refs"] == []
|
|
28
|
+
assert "CAP-" not in definition["method"]
|
|
29
|
+
assert "## Reporting Contract" not in definition["method"]
|
|
30
|
+
assert "skill_doc: `./SKILL.md`" in (repo / relative.parent / "meta.md").read_text()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_artifact_v1_explicit_batch_selection_and_knowledge_resolution():
|
|
34
|
+
repo = Path(__file__).resolve().parents[1]
|
|
35
|
+
paths = [Path(f"skills/{directory}/SKILL.v1.md") for directory, *_ in CASES]
|
|
36
|
+
catalog = XRefCatalog.build(repo, skill_definition_paths=paths)
|
|
37
|
+
ids = {case[1] for case in CASES}
|
|
38
|
+
entries = {entry.skill_id: entry for entry in catalog.skills if entry.skill_id in ids}
|
|
39
|
+
assert set(entries) == ids
|
|
40
|
+
assert all(entry.definition_format == "skill_definition_v1" for entry in entries.values())
|
|
41
|
+
for directory, skill_id, *_ in CASES:
|
|
42
|
+
definition = load_skill_definition(repo / f"skills/{directory}/SKILL.v1.md")
|
|
43
|
+
need_ids = [need["id"] for need in definition["metadata"]["knowledge_needs"]]
|
|
44
|
+
result = catalog.resolve_skill_knowledge(skill_id, need_ids)
|
|
45
|
+
assert result["unresolved_activation"] == []
|
|
46
|
+
assert result["unsatisfied_required"] == []
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import threading
|
|
3
|
+
from urllib.error import HTTPError
|
|
4
|
+
from urllib.request import Request, urlopen
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from pydantic import ValidationError
|
|
8
|
+
|
|
9
|
+
from xrefkit.attention_pet.evaluator import Weights, evaluate
|
|
10
|
+
from xrefkit.attention_pet.model import Conversation, Edge, Item, WorkingSet, extract
|
|
11
|
+
from xrefkit.attention_pet.scenarios import scenario
|
|
12
|
+
from xrefkit.attention_pet.server import make_server
|
|
13
|
+
from xrefkit.attention_pet.store import Store
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def fixture(name="A", step=1):
|
|
17
|
+
return WorkingSet.model_validate(scenario(name, step)["workingSet"])
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_scenario_a_independent_requests_grow_gradually():
|
|
21
|
+
scores = [evaluate(fixture("A", s))["ral"] for s in range(1, 6)]
|
|
22
|
+
assert scores == sorted(scores)
|
|
23
|
+
assert max(b - a for a, b in zip(scores, scores[1:])) < 10
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_scenario_b_relationships_cost_more_than_independent_items():
|
|
27
|
+
a, b = evaluate(fixture("A", 5)), evaluate(fixture("B", 5))
|
|
28
|
+
assert a["features"]["active_items"] == b["features"]["active_items"]
|
|
29
|
+
assert b["ral"] > a["ral"] + 20
|
|
30
|
+
assert b["features"]["dependency_edges"] > 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_scenario_c_low_load_can_have_wrong_trajectory():
|
|
34
|
+
states = [evaluate(fixture("C", s)) for s in range(1, 4)]
|
|
35
|
+
assert len({s["ral"] for s in states}) == 1
|
|
36
|
+
assert states[-1]["ral"] < 40
|
|
37
|
+
assert states[-1]["trajectoryStability"] < states[0]["trajectoryStability"]
|
|
38
|
+
assert states[-1]["state"] == "WRONG TRAJECTORY"
|
|
39
|
+
assert states[-1]["expression"] == "wrong_trajectory"
|
|
40
|
+
assert "restart" in states[-1]["recommendedActions"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_scenario_d_summary_lowers_load_without_erasing_constraints():
|
|
44
|
+
store = Store()
|
|
45
|
+
before = store.submit(fixture("D"))
|
|
46
|
+
result = store.recover("summarize", before["state"]["observedAt"])
|
|
47
|
+
assert before["state"]["ral"] >= 75
|
|
48
|
+
assert result["state"]["ral"] < before["state"]["ral"]
|
|
49
|
+
assert result["state"]["deltaRal"] < 0
|
|
50
|
+
assert result["state"]["features"]["constraint"] == before["state"]["features"]["constraint"]
|
|
51
|
+
assert result["state"]["features"]["dependency_edges"] == before["state"]["features"]["dependency_edges"]
|
|
52
|
+
assert len(result["workingSet"]["items"]) == len(before["workingSet"]["items"])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_scenario_e_restart_requires_fresh_validation_to_recover():
|
|
56
|
+
store = Store()
|
|
57
|
+
before = store.submit(fixture("E", 3))
|
|
58
|
+
handoff = store.recover("restart", before["state"]["observedAt"])
|
|
59
|
+
assert handoff["state"] == before["state"] # Download is not a context restart.
|
|
60
|
+
fresh = fixture("E", 4)
|
|
61
|
+
fresh.observations = [o for o in fresh.observations if o.context != fresh.contextId]
|
|
62
|
+
unknown = store.submit(fresh)
|
|
63
|
+
assert unknown["state"]["trajectoryStability"] is None
|
|
64
|
+
recovered = store.submit(fixture("E", 5))
|
|
65
|
+
assert recovered["state"]["trajectoryStability"] > before["state"]["trajectoryStability"]
|
|
66
|
+
assert recovered["state"]["ral"] == before["state"]["ral"]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_trend_uses_time_window_and_reports_feature_deltas():
|
|
70
|
+
store = Store()
|
|
71
|
+
a = store.submit(fixture("B", 1))
|
|
72
|
+
b = store.submit(fixture("B", 4))
|
|
73
|
+
assert b["state"]["deltaRal"] == b["state"]["ral"] - a["state"]["ral"]
|
|
74
|
+
assert b["state"]["trend"] == "rapid_rise"
|
|
75
|
+
assert {c["feature"] for c in b["state"]["changes"]} >= {"constraint", "dependency_edges"}
|
|
76
|
+
ws = fixture("B", 5)
|
|
77
|
+
ws.observedAt = 2000.0
|
|
78
|
+
assert store.submit(ws)["state"]["trend"] != "rapid_rise"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_unknown_trajectory_does_not_override_load_and_task_switch_does_not_compare():
|
|
82
|
+
ws = fixture()
|
|
83
|
+
ws.observations = []
|
|
84
|
+
assert evaluate(ws)["state"] == "UNKNOWN"
|
|
85
|
+
assert evaluate(ws)["expression"] == "stable"
|
|
86
|
+
store = Store()
|
|
87
|
+
store.submit(ws)
|
|
88
|
+
assert store.submit(fixture("B"))["state"]["deltaRal"] is None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize("coverage", ["partial", "reviewed"])
|
|
92
|
+
@pytest.mark.parametrize("step,expression", [(1,"stable"),(3,"loaded"),(5,"strained")])
|
|
93
|
+
def test_structural_load_remains_visible_without_trajectory(coverage, step, expression):
|
|
94
|
+
ws = fixture("B", step)
|
|
95
|
+
ws.coverage = coverage
|
|
96
|
+
ws.observations = []
|
|
97
|
+
state = evaluate(ws)
|
|
98
|
+
assert state["trajectoryStability"] is None
|
|
99
|
+
assert state["state"] == "UNKNOWN"
|
|
100
|
+
assert state["expression"] == expression
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_fix_retains_relations_and_does_not_fake_lower_load():
|
|
104
|
+
store = Store()
|
|
105
|
+
before = store.submit(fixture("B", 3))
|
|
106
|
+
fixed = store.recover("fix", before["state"]["observedAt"])
|
|
107
|
+
assert fixed["state"]["ral"] == before["state"]["ral"]
|
|
108
|
+
assert all(i["fixed"] for i in fixed["workingSet"]["items"] if i["kind"] == "decision")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_summary_preserves_history_referenced_by_active_items():
|
|
112
|
+
ws = fixture("D")
|
|
113
|
+
ws.dependencies.append(Edge(source="d0", target="h0-0", evidence="needed evidence"))
|
|
114
|
+
store = Store()
|
|
115
|
+
store.submit(ws)
|
|
116
|
+
result = store.recover("summarize", ws.observedAt)
|
|
117
|
+
assert next(i for i in result["workingSet"]["items"] if i["id"] == "h0-0")["status"] == "active"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@pytest.mark.parametrize("action", ["split", "externalize", "resolve", "rebase", "restart"])
|
|
121
|
+
def test_handoff_actions_do_not_claim_to_change_live_context(action):
|
|
122
|
+
store = Store()
|
|
123
|
+
before = store.submit(fixture())
|
|
124
|
+
result = store.recover(action, before["state"]["observedAt"])
|
|
125
|
+
assert result["recovery"]["effect"] == "handoff_created"
|
|
126
|
+
assert result["recovery"]["handoff"]["workingSet"] == before["workingSet"]
|
|
127
|
+
assert result["state"] == before["state"]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.mark.parametrize("mutation", [
|
|
131
|
+
lambda d: d.update(unexpected="field"),
|
|
132
|
+
lambda d: d.update(observedAt=float("nan")),
|
|
133
|
+
lambda d: d.update(items=d["items"] * 2),
|
|
134
|
+
lambda d: d.update(dependencies=[{"source":"absent","target":"d0","evidence":"bad"}]),
|
|
135
|
+
lambda d: d["items"][0].update(depth=-1),
|
|
136
|
+
lambda d: d["items"][0].update(fixed="true"),
|
|
137
|
+
])
|
|
138
|
+
def test_invalid_boundary_data_is_rejected(mutation):
|
|
139
|
+
data = fixture().model_dump()
|
|
140
|
+
mutation(data)
|
|
141
|
+
with pytest.raises(ValidationError):
|
|
142
|
+
WorkingSet.model_validate(data)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_timestamp_replay_and_stale_recovery():
|
|
146
|
+
store = Store()
|
|
147
|
+
ws = fixture()
|
|
148
|
+
first = store.submit(ws)
|
|
149
|
+
assert store.submit(ws) == first
|
|
150
|
+
stale = ws.model_copy(deep=True)
|
|
151
|
+
stale.observedAt = 1.0
|
|
152
|
+
with pytest.raises(ValueError, match="timestamps"):
|
|
153
|
+
store.submit(stale)
|
|
154
|
+
with pytest.raises(ValueError, match="refresh"):
|
|
155
|
+
store.recover("fix", 1.0)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_extract_upserts_status_and_marks_unannotated_turn_partial():
|
|
159
|
+
data = scenario("A")["conversation"]
|
|
160
|
+
updated = dict(data["turns"][0]["items"][0], status="done")
|
|
161
|
+
data["turns"].append({"id":"update", "text":"Requirement complete", "items":[updated]})
|
|
162
|
+
ws = extract(Conversation.model_validate(data))
|
|
163
|
+
assert len(ws.items) == 5 and ws.items[0].status == "done"
|
|
164
|
+
data["turns"].append({"id":"unannotated", "text":"A new unknown requirement"})
|
|
165
|
+
assert extract(Conversation.model_validate(data)).coverage == "partial"
|
|
166
|
+
data["turns"][0]["items"][0]["source"] = "missing"
|
|
167
|
+
with pytest.raises(ValueError, match="supplied turn"):
|
|
168
|
+
extract(Conversation.model_validate(data))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_persistence_restart_and_failed_write_do_not_mutate_state(tmp_path, monkeypatch):
|
|
172
|
+
from pathlib import Path
|
|
173
|
+
path = tmp_path / "session.json"
|
|
174
|
+
store = Store(path)
|
|
175
|
+
store.submit(fixture("B", 1))
|
|
176
|
+
before = store.submit(fixture("B", 2))
|
|
177
|
+
assert Store(path).view() == before
|
|
178
|
+
def fail(*args):
|
|
179
|
+
raise OSError("disk full")
|
|
180
|
+
monkeypatch.setattr(Path, "replace", fail)
|
|
181
|
+
with pytest.raises(OSError):
|
|
182
|
+
store.submit(fixture("B", 3))
|
|
183
|
+
assert store.view() == before
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_weights_validation():
|
|
187
|
+
with pytest.raises(ValueError):
|
|
188
|
+
Weights(working_set=-1)
|
|
189
|
+
with pytest.raises(ValueError):
|
|
190
|
+
Weights(working_set=float("nan"))
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_high_load_alone_never_recommends_restart():
|
|
194
|
+
state = evaluate(fixture("D"))
|
|
195
|
+
assert state["ral"] >= 75 and state["state"] == "HIGH LOAD"
|
|
196
|
+
assert "restart" not in state["recommendedActions"]
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@pytest.mark.parametrize("count,band", [(1,"stable"),(16,"loaded"),(24,"strained"),(30,"high_pressure"),(36,"unstable")])
|
|
200
|
+
def test_five_visual_bands_and_critical_override(count, band):
|
|
201
|
+
ws = fixture()
|
|
202
|
+
ws.items = [Item(id=f"g-{i}", kind="goal", text="goal", source="test") for i in range(count)]
|
|
203
|
+
weights = Weights(working_set=100, dependency_complexity=0, constraint_density=0,
|
|
204
|
+
decision_depth=0, conflict_pressure=0, context_dispersion=0)
|
|
205
|
+
assert evaluate(ws, weights=weights)["expression"] == band
|
|
206
|
+
ws.observations = fixture("C", 3).observations
|
|
207
|
+
result = evaluate(ws, weights=weights)
|
|
208
|
+
assert result["state"] == ("CRITICAL" if count >= 30 else "WRONG TRAJECTORY")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_empty_partial_snapshot_stays_unknown_and_bounds_hold():
|
|
212
|
+
ws = WorkingSet(taskId="empty",contextId="c",observedAt=1.0,items=[])
|
|
213
|
+
result = evaluate(ws)
|
|
214
|
+
assert result["ral"] == 0 and result["trajectoryStability"] is None
|
|
215
|
+
assert result["state"] == "UNKNOWN"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_http_api_auth_origin_validation_recovery_and_assets():
|
|
219
|
+
server, launch = make_server(Store())
|
|
220
|
+
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
221
|
+
thread.start()
|
|
222
|
+
base, token = launch.rstrip("/"), server.write_token
|
|
223
|
+
def request(path, data=None, auth=True, origin=None):
|
|
224
|
+
headers = {"Authorization":f"Bearer {token}"} if auth else {}
|
|
225
|
+
if origin:
|
|
226
|
+
headers["Origin"] = origin
|
|
227
|
+
if data is not None:
|
|
228
|
+
headers["Content-Type"] = "application/json"
|
|
229
|
+
req = Request(base + path, json.dumps(data).encode() if data is not None else None, headers)
|
|
230
|
+
with urlopen(req, timeout=5) as response:
|
|
231
|
+
return response.read()
|
|
232
|
+
try:
|
|
233
|
+
assert b"Attention Pet" in request("/", auth=False)
|
|
234
|
+
for path in ("/pet.js", "/pet.css"):
|
|
235
|
+
assert request(path, auth=False)
|
|
236
|
+
assert json.loads(request("/api/state", auth=False))["state"] is None
|
|
237
|
+
with pytest.raises(HTTPError) as error:
|
|
238
|
+
request("/api/snapshot", fixture().model_dump(), auth=False)
|
|
239
|
+
assert error.value.code == 401
|
|
240
|
+
with pytest.raises(HTTPError) as error:
|
|
241
|
+
request("/api/snapshot", fixture().model_dump(), origin="https://example.com")
|
|
242
|
+
assert error.value.code == 403
|
|
243
|
+
with pytest.raises(HTTPError) as error:
|
|
244
|
+
request("/api/snapshot", {"ral":99})
|
|
245
|
+
assert error.value.code == 400
|
|
246
|
+
before = json.loads(request("/api/conversation", scenario("D")["conversation"]))
|
|
247
|
+
after = json.loads(request("/api/recover", {"action":"summarize", "expectedObservedAt":before["state"]["observedAt"]}))
|
|
248
|
+
assert after["state"]["ral"] < before["state"]["ral"]
|
|
249
|
+
assert json.loads(request("/api/state"))["state"] == after["state"]
|
|
250
|
+
with pytest.raises(HTTPError) as error:
|
|
251
|
+
request("/api/demo", {"scenario":"C", "step":3})
|
|
252
|
+
assert error.value.code == 404
|
|
253
|
+
assert json.loads(request("/api/state"))["state"] == after["state"]
|
|
254
|
+
finally:
|
|
255
|
+
server.shutdown()
|
|
256
|
+
server.server_close()
|
|
257
|
+
thread.join(timeout=5)
|