xrefkit 0.4.1__tar.gz → 0.4.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.4.1 → xrefkit-0.4.3}/PKG-INFO +4 -1
- {xrefkit-0.4.1 → xrefkit-0.4.3}/README.md +3 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/pyproject.toml +1 -1
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_cli.py +22 -10
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_dashboard.py +2 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_instruction_workflow.py +72 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_skill_runtime_audit.py +4 -1
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/__init__.py +1 -1
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/operations_cli.py +3 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/skillrun.py +85 -5
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit.egg-info/PKG-INFO +4 -1
- {xrefkit-0.4.1 → xrefkit-0.4.3}/LICENSE +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/setup.cfg +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_base_sync_ownership.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_boundary_analysis.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_calibration_lint.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_check_skill_knowledge_xids.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_collect_analyzer_sarif.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_convert_to_xrefkit_skill.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_cs_scope_probe.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_csharp_commonality.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_csharp_naming_profile.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_ctx.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_cutover_readiness.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_error_policy_audit.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_error_policy_locator.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_fm_multiroot.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_gate.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_goal_desired_state.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_knowledge_relations_validator.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_ownership.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_packmeta.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_project_quality_baseline.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_resource_provider.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_runtime_contracts.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_sarif_to_locator.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_skillmeta.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_structure_catalog.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_xref.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_xrefkit_instance.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_xrefkit_tools.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_xrefkit_v2_discovery.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_xrefkit_v2_models.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/tests/test_xrefkit_v2_pipeline.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/__main__.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/boundary_analysis.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/catalog_cli.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/cli.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/contracts.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/ctx.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/dashboard.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/discovery.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/gate.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/goalstate.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/hashing.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/import_skill.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/instance.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/loaders.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/__init__.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/audit.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/bootstrap.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/catalog.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/cli.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/client_cache.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/context_registry.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/contracts.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/dist.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/ownership.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/repository.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/schemas.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/server.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp/startup_contract_pack.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/mcp_tools.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/__init__.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/common.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/effective_bundle.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/local_manifest.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/package_manifest.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/run_log.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/server_config.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/models/skill_definition.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/ownership.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/packmeta.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/registry.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resolver.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resource_provider.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/contracts.json +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/current.json +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/model_body.md +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/runlog.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/skillmeta.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/structure_catalog.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/tools/__init__.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/tools/__main__.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/v2_cli.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/workspace.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/xref.py +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit.egg-info/SOURCES.txt +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit.egg-info/dependency_links.txt +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit.egg-info/entry_points.txt +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit.egg-info/requires.txt +0 -0
- {xrefkit-0.4.1 → xrefkit-0.4.3}/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.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
|
|
5
5
|
Author: synthaicode
|
|
6
6
|
License: MIT License
|
|
@@ -120,6 +120,9 @@ xrefkit workflow run --task "Perform the requested procedure" `
|
|
|
120
120
|
|
|
121
121
|
`verify` and `close` determine procedural completion only. Output quality is
|
|
122
122
|
recorded separately after human acceptance with the existing feedback record.
|
|
123
|
+
Each work item also requires its own completion criterion; if that criterion is
|
|
124
|
+
not yet definable, record the item as unknown, blocked, or escalated with a
|
|
125
|
+
reason instead of inventing a criterion.
|
|
123
126
|
|
|
124
127
|
## Quick Start
|
|
125
128
|
|
|
@@ -81,6 +81,9 @@ xrefkit workflow run --task "Perform the requested procedure" `
|
|
|
81
81
|
|
|
82
82
|
`verify` and `close` determine procedural completion only. Output quality is
|
|
83
83
|
recorded separately after human acceptance with the existing feedback record.
|
|
84
|
+
Each work item also requires its own completion criterion; if that criterion is
|
|
85
|
+
not yet definable, record the item as unknown, blocked, or escalated with a
|
|
86
|
+
reason instead of inventing a criterion.
|
|
84
87
|
|
|
85
88
|
## Quick Start
|
|
86
89
|
|
|
@@ -82,6 +82,8 @@ class CliTests(unittest.TestCase):
|
|
|
82
82
|
"WI-001",
|
|
83
83
|
"--text",
|
|
84
84
|
"Implement controlled output",
|
|
85
|
+
"--completion-criterion",
|
|
86
|
+
"output is written and validated",
|
|
85
87
|
"--status",
|
|
86
88
|
"done",
|
|
87
89
|
"--role",
|
|
@@ -739,6 +741,8 @@ class CliTests(unittest.TestCase):
|
|
|
739
741
|
"WI-001",
|
|
740
742
|
"--text",
|
|
741
743
|
"Implement controlled output",
|
|
744
|
+
"--completion-criterion",
|
|
745
|
+
"output is written and validated",
|
|
742
746
|
"--status",
|
|
743
747
|
"in_progress",
|
|
744
748
|
"--role",
|
|
@@ -765,6 +769,8 @@ class CliTests(unittest.TestCase):
|
|
|
765
769
|
"WI-001",
|
|
766
770
|
"--status",
|
|
767
771
|
"done",
|
|
772
|
+
"--completion-criterion",
|
|
773
|
+
"output is written and validated",
|
|
768
774
|
"--role",
|
|
769
775
|
"sample_skill:executor",
|
|
770
776
|
]
|
|
@@ -772,7 +778,7 @@ class CliTests(unittest.TestCase):
|
|
|
772
778
|
)
|
|
773
779
|
|
|
774
780
|
text = out.read_text(encoding="utf-8")
|
|
775
|
-
self.assertIn("
|
|
781
|
+
self.assertIn("WI-001 status=`done` role=`sample_skill:executor` criterion=`output is written and validated`", text)
|
|
776
782
|
|
|
777
783
|
def test_main_skill_close_rejects_pending_concrete_work_item(self) -> None:
|
|
778
784
|
with tempfile.TemporaryDirectory() as tmp:
|
|
@@ -808,9 +814,11 @@ class CliTests(unittest.TestCase):
|
|
|
808
814
|
str(out),
|
|
809
815
|
"--item",
|
|
810
816
|
"WI-001",
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
817
|
+
"--text",
|
|
818
|
+
"Implement controlled output",
|
|
819
|
+
"--completion-criterion",
|
|
820
|
+
"output is written and validated",
|
|
821
|
+
"--status",
|
|
814
822
|
"pending",
|
|
815
823
|
"--role",
|
|
816
824
|
"sample_skill:executor",
|
|
@@ -1065,9 +1073,11 @@ class CliTests(unittest.TestCase):
|
|
|
1065
1073
|
str(out),
|
|
1066
1074
|
"--item",
|
|
1067
1075
|
"WI-001",
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1076
|
+
"--text",
|
|
1077
|
+
"Implement controlled output",
|
|
1078
|
+
"--completion-criterion",
|
|
1079
|
+
"output is written and validated",
|
|
1080
|
+
"--status",
|
|
1071
1081
|
"done",
|
|
1072
1082
|
"--role",
|
|
1073
1083
|
"sample_skill:executor",
|
|
@@ -1375,9 +1385,11 @@ class CliTests(unittest.TestCase):
|
|
|
1375
1385
|
str(out),
|
|
1376
1386
|
"--item",
|
|
1377
1387
|
"WI-001",
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1388
|
+
"--text",
|
|
1389
|
+
"Implement controlled output",
|
|
1390
|
+
"--completion-criterion",
|
|
1391
|
+
"output is written and validated",
|
|
1392
|
+
"--status",
|
|
1381
1393
|
"done",
|
|
1382
1394
|
"--role",
|
|
1383
1395
|
"sample_skill:executor",
|
|
@@ -48,6 +48,8 @@ def test_instruction_workflow_uses_default_conditions_and_shared_protocol(tmp_pa
|
|
|
48
48
|
"WI-001",
|
|
49
49
|
"--text",
|
|
50
50
|
"Perform the instruction",
|
|
51
|
+
"--completion-criterion",
|
|
52
|
+
"instruction result is recorded and verified",
|
|
51
53
|
"--status",
|
|
52
54
|
"done",
|
|
53
55
|
"--role",
|
|
@@ -98,3 +100,73 @@ def test_instruction_workflow_uses_default_conditions_and_shared_protocol(tmp_pa
|
|
|
98
100
|
) == 0
|
|
99
101
|
assert _run(tmp_path, "skill", "close", "--log", str(out)) == 0
|
|
100
102
|
assert "## Completion Conditions" in out.read_text(encoding="utf-8")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_workitem_requires_criterion_or_explicit_unknown_reason(tmp_path: Path) -> None:
|
|
106
|
+
out = tmp_path / "work" / "sessions" / "run.md"
|
|
107
|
+
assert _run(
|
|
108
|
+
tmp_path,
|
|
109
|
+
"workflow",
|
|
110
|
+
"run",
|
|
111
|
+
"--task",
|
|
112
|
+
"Do work",
|
|
113
|
+
"--out",
|
|
114
|
+
str(out),
|
|
115
|
+
"--use-default-completion-conditions",
|
|
116
|
+
) == 0
|
|
117
|
+
assert _run(
|
|
118
|
+
tmp_path,
|
|
119
|
+
"skill",
|
|
120
|
+
"workitem",
|
|
121
|
+
"--log",
|
|
122
|
+
str(out),
|
|
123
|
+
"--item",
|
|
124
|
+
"WI-001",
|
|
125
|
+
"--text",
|
|
126
|
+
"Investigate missing requirement",
|
|
127
|
+
"--status",
|
|
128
|
+
"pending",
|
|
129
|
+
"--role",
|
|
130
|
+
"instruction:executor",
|
|
131
|
+
) == 1
|
|
132
|
+
assert _run(
|
|
133
|
+
tmp_path,
|
|
134
|
+
"skill",
|
|
135
|
+
"workitem",
|
|
136
|
+
"--log",
|
|
137
|
+
str(out),
|
|
138
|
+
"--item",
|
|
139
|
+
"WI-001",
|
|
140
|
+
"--text",
|
|
141
|
+
"Investigate missing requirement",
|
|
142
|
+
"--status",
|
|
143
|
+
"unknown",
|
|
144
|
+
"--criterion-unknown-reason",
|
|
145
|
+
"The business owner has not defined the acceptance outcome",
|
|
146
|
+
"--role",
|
|
147
|
+
"instruction:executor",
|
|
148
|
+
) == 0
|
|
149
|
+
text = out.read_text(encoding="utf-8")
|
|
150
|
+
assert "criterion=`` reason=`The business owner has not defined the acceptance outcome`" in text
|
|
151
|
+
assert _run(tmp_path, "skill", "phase", "--log", str(out), "--phase", "execution", "--status", "done", "--role", "instruction:executor") == 0
|
|
152
|
+
assert _run(tmp_path, "skill", "verify", "--log", str(out)) == 1
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_workitem_criterion_is_immutable_and_changes_use_supersedes(tmp_path: Path) -> None:
|
|
156
|
+
out = tmp_path / "work" / "sessions" / "run.md"
|
|
157
|
+
assert _run(tmp_path, "workflow", "run", "--task", "Do work", "--out", str(out), "--use-default-completion-conditions") == 0
|
|
158
|
+
base = [
|
|
159
|
+
"skill", "workitem", "--log", str(out), "--item", "WI-001",
|
|
160
|
+
"--text", "Implement original outcome", "--completion-criterion", "original outcome is verified",
|
|
161
|
+
"--status", "pending", "--role", "instruction:executor",
|
|
162
|
+
]
|
|
163
|
+
assert _run(tmp_path, *base) == 0
|
|
164
|
+
assert _run(tmp_path, *base[:-6], "--completion-criterion", "different outcome is verified", "--status", "pending", "--role", "instruction:executor") == 1
|
|
165
|
+
assert _run(
|
|
166
|
+
tmp_path,
|
|
167
|
+
"skill", "workitem", "--log", str(out), "--item", "WI-002", "--supersedes", "WI-001",
|
|
168
|
+
"--text", "Implement revised outcome", "--completion-criterion", "revised outcome is verified",
|
|
169
|
+
"--status", "pending", "--role", "instruction:executor",
|
|
170
|
+
) == 0
|
|
171
|
+
text = out.read_text(encoding="utf-8")
|
|
172
|
+
assert "WI-002" in text and "supersedes=`WI-001`" in text
|
|
@@ -84,6 +84,8 @@ class SkillRuntimeAuditTests(unittest.TestCase):
|
|
|
84
84
|
"WI-001",
|
|
85
85
|
"--text",
|
|
86
86
|
"Implement controlled output",
|
|
87
|
+
"--completion-criterion",
|
|
88
|
+
"output is written and validated",
|
|
87
89
|
"--status",
|
|
88
90
|
"done",
|
|
89
91
|
"--role",
|
|
@@ -322,6 +324,7 @@ class SkillRuntimeAuditTests(unittest.TestCase):
|
|
|
322
324
|
[
|
|
323
325
|
"skill", "workitem", "--log", str(out), "--item", "WI-001",
|
|
324
326
|
"--text", "Implement controlled output", "--status", "done",
|
|
327
|
+
"--completion-criterion", "output is written and validated",
|
|
325
328
|
"--role", "sample_skill:executor",
|
|
326
329
|
]
|
|
327
330
|
),
|
|
@@ -393,7 +396,7 @@ class SkillRuntimeAuditTests(unittest.TestCase):
|
|
|
393
396
|
main(
|
|
394
397
|
[
|
|
395
398
|
"skill", "workitem", "--log", str(out), "--item", "WI-001",
|
|
396
|
-
"--text", "do work", "--status", "done", "--role", "sample_skill:executor",
|
|
399
|
+
"--text", "do work", "--status", "done", "--completion-criterion", "work is verified", "--role", "sample_skill:executor",
|
|
397
400
|
]
|
|
398
401
|
),
|
|
399
402
|
)
|
|
@@ -497,6 +497,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
497
497
|
p_skill_workitem.add_argument("--log", required=True, help="Skill run log to update")
|
|
498
498
|
p_skill_workitem.add_argument("--item", required=True, help="Stable work item id, such as WI-001")
|
|
499
499
|
p_skill_workitem.add_argument("--text", default=None, help="Work item text; required when adding a new item")
|
|
500
|
+
p_skill_workitem.add_argument("--completion-criterion", default=None, help="Observable procedural condition for this work item")
|
|
501
|
+
p_skill_workitem.add_argument("--criterion-unknown-reason", default=None, help="Why the completion criterion cannot yet be defined for unknown/blocked/escalated work")
|
|
502
|
+
p_skill_workitem.add_argument("--supersedes", default=None, help="Existing work item whose criterion is being replaced by this new item")
|
|
500
503
|
p_skill_workitem.add_argument(
|
|
501
504
|
"--status",
|
|
502
505
|
required=True,
|
|
@@ -179,6 +179,17 @@ WORKLIST_ROWS = [
|
|
|
179
179
|
("Handoff", "Record outputs, unresolved items, next owner, and human decision points."),
|
|
180
180
|
]
|
|
181
181
|
WORKITEM_RE = re.compile(
|
|
182
|
+
r"^- \[(?P<checkbox>[ x!])\] (?P<item_id>[A-Za-z0-9_.-]+) "
|
|
183
|
+
r"status=`(?P<status>[^`]+)` role=`(?P<role>[^`]+)` "
|
|
184
|
+
r"criterion=`(?P<criterion>[^`]*)` reason=`(?P<reason>[^`]*)` "
|
|
185
|
+
r"supersedes=`(?P<supersedes>[^`]*)`: (?P<text>.*)$"
|
|
186
|
+
)
|
|
187
|
+
WORKITEM_V2_RE = re.compile(
|
|
188
|
+
r"^- \[(?P<checkbox>[ x!])\] (?P<item_id>[A-Za-z0-9_.-]+) "
|
|
189
|
+
r"status=`(?P<status>[^`]+)` role=`(?P<role>[^`]+)` "
|
|
190
|
+
r"criterion=`(?P<criterion>[^`]*)` reason=`(?P<reason>[^`]*)`: (?P<text>.*)$"
|
|
191
|
+
)
|
|
192
|
+
LEGACY_WORKITEM_RE = re.compile(
|
|
182
193
|
r"^- \[(?P<checkbox>[ x!])\] (?P<item_id>[A-Za-z0-9_.-]+) "
|
|
183
194
|
r"status=`(?P<status>[^`]+)` role=`(?P<role>[^`]+)`: (?P<text>.*)$"
|
|
184
195
|
)
|
|
@@ -825,6 +836,34 @@ def _parse_work_items(text: str) -> list[dict[str, str]]:
|
|
|
825
836
|
items: list[dict[str, str]] = []
|
|
826
837
|
for line in body.splitlines():
|
|
827
838
|
match = WORKITEM_RE.match(line)
|
|
839
|
+
if match:
|
|
840
|
+
items.append(
|
|
841
|
+
{
|
|
842
|
+
"item_id": match.group("item_id"),
|
|
843
|
+
"status": match.group("status"),
|
|
844
|
+
"role": match.group("role"),
|
|
845
|
+
"criterion": match.group("criterion"),
|
|
846
|
+
"reason": match.group("reason"),
|
|
847
|
+
"supersedes": match.group("supersedes"),
|
|
848
|
+
"text": match.group("text"),
|
|
849
|
+
}
|
|
850
|
+
)
|
|
851
|
+
continue
|
|
852
|
+
match = WORKITEM_V2_RE.match(line)
|
|
853
|
+
if match:
|
|
854
|
+
items.append(
|
|
855
|
+
{
|
|
856
|
+
"item_id": match.group("item_id"),
|
|
857
|
+
"status": match.group("status"),
|
|
858
|
+
"role": match.group("role"),
|
|
859
|
+
"criterion": match.group("criterion"),
|
|
860
|
+
"reason": match.group("reason"),
|
|
861
|
+
"supersedes": "",
|
|
862
|
+
"text": match.group("text"),
|
|
863
|
+
}
|
|
864
|
+
)
|
|
865
|
+
continue
|
|
866
|
+
match = LEGACY_WORKITEM_RE.match(line)
|
|
828
867
|
if not match:
|
|
829
868
|
continue
|
|
830
869
|
items.append(
|
|
@@ -832,14 +871,20 @@ def _parse_work_items(text: str) -> list[dict[str, str]]:
|
|
|
832
871
|
"item_id": match.group("item_id"),
|
|
833
872
|
"status": match.group("status"),
|
|
834
873
|
"role": match.group("role"),
|
|
874
|
+
"criterion": "",
|
|
875
|
+
"reason": "legacy work item has no recorded completion criterion",
|
|
876
|
+
"supersedes": "",
|
|
835
877
|
"text": match.group("text"),
|
|
836
878
|
}
|
|
837
879
|
)
|
|
838
880
|
return items
|
|
839
881
|
|
|
840
882
|
|
|
841
|
-
def _render_workitem_line(*, item_id: str, status: str, role: str, text: str) -> str:
|
|
842
|
-
return
|
|
883
|
+
def _render_workitem_line(*, item_id: str, status: str, role: str, criterion: str, reason: str, supersedes: str, text: str) -> str:
|
|
884
|
+
return (
|
|
885
|
+
f"- [{_workitem_checkbox(status)}] {item_id} status=`{status}` role=`{role}` "
|
|
886
|
+
f"criterion=`{criterion}` reason=`{reason}` supersedes=`{supersedes}`: {text}"
|
|
887
|
+
)
|
|
843
888
|
|
|
844
889
|
|
|
845
890
|
def _overall_workitem_status(items: list[dict[str, str]]) -> str:
|
|
@@ -860,7 +905,7 @@ def _replace_concrete_work_items_section(text: str, items: list[dict[str, str]])
|
|
|
860
905
|
insert_at = text.find("\n## Execution Role")
|
|
861
906
|
if insert_at == -1:
|
|
862
907
|
insert_at = len(text)
|
|
863
|
-
section = "\n\n## Concrete Work Items\n\n- status: `pending`\n- rule:
|
|
908
|
+
section = "\n\n## Concrete Work Items\n\n- status: `pending`\n- rule: each work item requires a completion criterion; use unknown, blocked, or escalated with a reason when the criterion cannot yet be defined\n"
|
|
864
909
|
text = text[:insert_at] + section + text[insert_at:]
|
|
865
910
|
body, start, end = _section_body(text, "Concrete Work Items")
|
|
866
911
|
if body is None:
|
|
@@ -871,7 +916,7 @@ def _replace_concrete_work_items_section(text: str, items: list[dict[str, str]])
|
|
|
871
916
|
"## Concrete Work Items",
|
|
872
917
|
"",
|
|
873
918
|
f"- status: `{status}`",
|
|
874
|
-
"- rule:
|
|
919
|
+
"- rule: each work item requires a completion criterion; use unknown, blocked, or escalated with a reason when the criterion cannot yet be defined",
|
|
875
920
|
]
|
|
876
921
|
lines.extend(_render_workitem_line(**item) for item in items)
|
|
877
922
|
new_body = "\n".join(lines) + "\n"
|
|
@@ -888,12 +933,19 @@ def update_work_item(args) -> SkillRunResult:
|
|
|
888
933
|
status = str(args.status).lower()
|
|
889
934
|
role = str(args.role).strip()
|
|
890
935
|
item_text = str(args.text or "").strip()
|
|
936
|
+
criterion = str(getattr(args, "completion_criterion", None) or "").strip().replace("`", "'").replace("\n", " ")
|
|
937
|
+
reason = str(getattr(args, "criterion_unknown_reason", None) or "").strip().replace("`", "'").replace("\n", " ")
|
|
938
|
+
supersedes = str(getattr(args, "supersedes", None) or "").strip().replace("`", "'").replace("\n", " ")
|
|
891
939
|
if not item_id:
|
|
892
940
|
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=["missing --item"])
|
|
893
941
|
if status not in VALID_WORKITEM_STATUSES:
|
|
894
942
|
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=[f"invalid work item status: {status}"])
|
|
895
943
|
if not role:
|
|
896
944
|
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=["missing --role"])
|
|
945
|
+
if not criterion and not reason and status in {"unknown", "blocked", "escalated"}:
|
|
946
|
+
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=["completion criterion is undefined; provide --criterion-unknown-reason for unknown, blocked, or escalated work items"])
|
|
947
|
+
if not criterion and status in {"pending", "in_progress", "done"}:
|
|
948
|
+
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=["--completion-criterion is required for pending, in_progress, and done work items"])
|
|
897
949
|
|
|
898
950
|
text = log_path.read_text(encoding="utf-8")
|
|
899
951
|
if not _has_opened_run_gate(text):
|
|
@@ -902,14 +954,31 @@ def update_work_item(args) -> SkillRunResult:
|
|
|
902
954
|
items = _parse_work_items(text)
|
|
903
955
|
existing = next((item for item in items if item["item_id"] == item_id), None)
|
|
904
956
|
if existing:
|
|
957
|
+
existing_criterion = existing.get("criterion", "")
|
|
958
|
+
if criterion and criterion != existing_criterion:
|
|
959
|
+
return SkillRunResult(
|
|
960
|
+
ok=False,
|
|
961
|
+
skill_id=None,
|
|
962
|
+
skill_doc=None,
|
|
963
|
+
run_log=str(log_path),
|
|
964
|
+
errors=[
|
|
965
|
+
f"completion criterion for {item_id} is immutable; create a new work item with --supersedes {item_id}"
|
|
966
|
+
],
|
|
967
|
+
)
|
|
968
|
+
if supersedes:
|
|
969
|
+
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=[f"--supersedes is only valid when creating a new work item, not updating {item_id}"])
|
|
905
970
|
existing["status"] = status
|
|
906
971
|
existing["role"] = role
|
|
972
|
+
if reason:
|
|
973
|
+
existing["reason"] = reason
|
|
907
974
|
if item_text:
|
|
908
975
|
existing["text"] = item_text
|
|
909
976
|
else:
|
|
910
977
|
if not item_text:
|
|
911
978
|
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=["new work item requires --text"])
|
|
912
|
-
|
|
979
|
+
if supersedes and not any(item["item_id"] == supersedes for item in items):
|
|
980
|
+
return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=[f"superseded work item not found: {supersedes}"])
|
|
981
|
+
items.append({"item_id": item_id, "status": status, "role": role, "criterion": criterion, "reason": reason, "supersedes": supersedes, "text": item_text})
|
|
913
982
|
|
|
914
983
|
text = _replace_concrete_work_items_section(text, items)
|
|
915
984
|
text = _append_phase_event(text, phase=f"workitem:{item_id}", status=status, role=role, note=item_text or None)
|
|
@@ -1436,6 +1505,17 @@ def _progression_record_errors(
|
|
|
1436
1505
|
if not work_items:
|
|
1437
1506
|
errors.append("at least one concrete work item is required before closure")
|
|
1438
1507
|
for item in work_items:
|
|
1508
|
+
if item.get("supersedes") and not any(previous["item_id"] == item["supersedes"] for previous in work_items):
|
|
1509
|
+
errors.append(f"work item {item['item_id']} supersedes missing work item {item['supersedes']}")
|
|
1510
|
+
if item.get("supersedes") == item["item_id"]:
|
|
1511
|
+
errors.append(f"work item {item['item_id']} cannot supersede itself")
|
|
1512
|
+
if not item.get("criterion"):
|
|
1513
|
+
if item["status"] in {"unknown", "blocked", "escalated"} and item.get("reason"):
|
|
1514
|
+
pass
|
|
1515
|
+
else:
|
|
1516
|
+
errors.append(f"work item {item['item_id']} must record a completion criterion or a reason why it cannot be defined")
|
|
1517
|
+
if item.get("criterion") == "unknown" and item["status"] in {"pending", "in_progress", "done"}:
|
|
1518
|
+
errors.append(f"work item {item['item_id']} cannot use unknown as its completion criterion while executable")
|
|
1439
1519
|
if item["status"] not in ACCEPTED_CLOSE_STATUSES:
|
|
1440
1520
|
errors.append(
|
|
1441
1521
|
f"work item {item['item_id']} must be done or escalated before closure; current={item['status']}"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xrefkit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit
|
|
5
5
|
Author: synthaicode
|
|
6
6
|
License: MIT License
|
|
@@ -120,6 +120,9 @@ xrefkit workflow run --task "Perform the requested procedure" `
|
|
|
120
120
|
|
|
121
121
|
`verify` and `close` determine procedural completion only. Output quality is
|
|
122
122
|
recorded separately after human acceptance with the existing feedback record.
|
|
123
|
+
Each work item also requires its own completion criterion; if that criterion is
|
|
124
|
+
not yet definable, record the item as unknown, blocked, or escalated with a
|
|
125
|
+
reason instead of inventing a criterion.
|
|
123
126
|
|
|
124
127
|
## Quick Start
|
|
125
128
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json
RENAMED
|
File without changes
|
{xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md
RENAMED
|
File without changes
|
{xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json
RENAMED
|
File without changes
|
{xrefkit-0.4.1 → xrefkit-0.4.3}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|