xrefkit 0.4.2__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.
Files changed (106) hide show
  1. {xrefkit-0.4.2 → xrefkit-0.4.3}/PKG-INFO +1 -1
  2. {xrefkit-0.4.2 → xrefkit-0.4.3}/pyproject.toml +1 -1
  3. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_instruction_workflow.py +20 -0
  4. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/__init__.py +1 -1
  5. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/operations_cli.py +1 -0
  6. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/skillrun.py +45 -5
  7. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit.egg-info/PKG-INFO +1 -1
  8. {xrefkit-0.4.2 → xrefkit-0.4.3}/LICENSE +0 -0
  9. {xrefkit-0.4.2 → xrefkit-0.4.3}/README.md +0 -0
  10. {xrefkit-0.4.2 → xrefkit-0.4.3}/setup.cfg +0 -0
  11. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_base_sync_ownership.py +0 -0
  12. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_boundary_analysis.py +0 -0
  13. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_calibration_lint.py +0 -0
  14. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_check_skill_knowledge_xids.py +0 -0
  15. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_cli.py +0 -0
  16. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_collect_analyzer_sarif.py +0 -0
  17. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_convert_to_xrefkit_skill.py +0 -0
  18. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_cs_scope_probe.py +0 -0
  19. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_csharp_commonality.py +0 -0
  20. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_csharp_naming_profile.py +0 -0
  21. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_ctx.py +0 -0
  22. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_cutover_readiness.py +0 -0
  23. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_dashboard.py +0 -0
  24. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_error_policy_audit.py +0 -0
  25. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_error_policy_locator.py +0 -0
  26. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_fm_multiroot.py +0 -0
  27. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_gate.py +0 -0
  28. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_goal_desired_state.py +0 -0
  29. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_knowledge_relations_validator.py +0 -0
  30. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_ownership.py +0 -0
  31. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_packmeta.py +0 -0
  32. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_project_quality_baseline.py +0 -0
  33. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_resource_provider.py +0 -0
  34. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_runtime_contracts.py +0 -0
  35. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_sarif_to_locator.py +0 -0
  36. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_skill_runtime_audit.py +0 -0
  37. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_skillmeta.py +0 -0
  38. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_structure_catalog.py +0 -0
  39. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_xref.py +0 -0
  40. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_xrefkit_instance.py +0 -0
  41. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_xrefkit_tools.py +0 -0
  42. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_xrefkit_v2_discovery.py +0 -0
  43. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_xrefkit_v2_models.py +0 -0
  44. {xrefkit-0.4.2 → xrefkit-0.4.3}/tests/test_xrefkit_v2_pipeline.py +0 -0
  45. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/__main__.py +0 -0
  46. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/boundary_analysis.py +0 -0
  47. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/catalog_cli.py +0 -0
  48. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/cli.py +0 -0
  49. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/contracts.py +0 -0
  50. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/ctx.py +0 -0
  51. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/dashboard.py +0 -0
  52. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/discovery.py +0 -0
  53. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/gate.py +0 -0
  54. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/goalstate.py +0 -0
  55. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/hashing.py +0 -0
  56. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/import_skill.py +0 -0
  57. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/instance.py +0 -0
  58. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/loaders.py +0 -0
  59. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/__init__.py +0 -0
  60. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/audit.py +0 -0
  61. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/bootstrap.py +0 -0
  62. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/catalog.py +0 -0
  63. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/cli.py +0 -0
  64. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/client_cache.py +0 -0
  65. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/context_registry.py +0 -0
  66. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/contracts.py +0 -0
  67. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/dist.py +0 -0
  68. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/ownership.py +0 -0
  69. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/repository.py +0 -0
  70. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/schemas.py +0 -0
  71. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/server.py +0 -0
  72. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp/startup_contract_pack.py +0 -0
  73. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/mcp_tools.py +0 -0
  74. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/__init__.py +0 -0
  75. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/common.py +0 -0
  76. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/effective_bundle.py +0 -0
  77. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/local_manifest.py +0 -0
  78. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/package_manifest.py +0 -0
  79. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/run_log.py +0 -0
  80. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/server_config.py +0 -0
  81. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/models/skill_definition.py +0 -0
  82. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/ownership.py +0 -0
  83. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/packmeta.py +0 -0
  84. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/registry.py +0 -0
  85. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resolver.py +0 -0
  86. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resource_provider.py +0 -0
  87. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resources/base/contracts.json +0 -0
  88. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resources/base/current.json +0 -0
  89. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resources/base/generations/7a682a5272907354/contracts.json +0 -0
  90. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resources/base/generations/7a682a5272907354/model_body.md +0 -0
  91. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +0 -0
  92. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +0 -0
  93. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/resources/base/model_body.md +0 -0
  94. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/runlog.py +0 -0
  95. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/skillmeta.py +0 -0
  96. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/structure_catalog.py +0 -0
  97. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/tools/__init__.py +0 -0
  98. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/tools/__main__.py +0 -0
  99. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/v2_cli.py +0 -0
  100. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/workspace.py +0 -0
  101. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit/xref.py +0 -0
  102. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit.egg-info/SOURCES.txt +0 -0
  103. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit.egg-info/dependency_links.txt +0 -0
  104. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit.egg-info/entry_points.txt +0 -0
  105. {xrefkit-0.4.2 → xrefkit-0.4.3}/xrefkit.egg-info/requires.txt +0 -0
  106. {xrefkit-0.4.2 → 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.2
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xrefkit"
7
- version = "0.4.2"
7
+ version = "0.4.3"
8
8
  description = "Portable XID, Skill, Knowledge, workflow, and MCP runtime for XRefKit"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -150,3 +150,23 @@ def test_workitem_requires_criterion_or_explicit_unknown_reason(tmp_path: Path)
150
150
  assert "criterion=`` reason=`The business owner has not defined the acceptance outcome`" in text
151
151
  assert _run(tmp_path, "skill", "phase", "--log", str(out), "--phase", "execution", "--status", "done", "--role", "instruction:executor") == 0
152
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
@@ -2,4 +2,4 @@
2
2
 
3
3
  __all__ = ["__version__"]
4
4
 
5
- __version__ = "0.4.2"
5
+ __version__ = "0.4.3"
@@ -499,6 +499,7 @@ def _build_parser() -> argparse.ArgumentParser:
499
499
  p_skill_workitem.add_argument("--text", default=None, help="Work item text; required when adding a new item")
500
500
  p_skill_workitem.add_argument("--completion-criterion", default=None, help="Observable procedural condition for this work item")
501
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")
502
503
  p_skill_workitem.add_argument(
503
504
  "--status",
504
505
  required=True,
@@ -179,6 +179,12 @@ 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(
182
188
  r"^- \[(?P<checkbox>[ x!])\] (?P<item_id>[A-Za-z0-9_.-]+) "
183
189
  r"status=`(?P<status>[^`]+)` role=`(?P<role>[^`]+)` "
184
190
  r"criterion=`(?P<criterion>[^`]*)` reason=`(?P<reason>[^`]*)`: (?P<text>.*)$"
@@ -838,6 +844,21 @@ def _parse_work_items(text: str) -> list[dict[str, str]]:
838
844
  "role": match.group("role"),
839
845
  "criterion": match.group("criterion"),
840
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": "",
841
862
  "text": match.group("text"),
842
863
  }
843
864
  )
@@ -852,16 +873,17 @@ def _parse_work_items(text: str) -> list[dict[str, str]]:
852
873
  "role": match.group("role"),
853
874
  "criterion": "",
854
875
  "reason": "legacy work item has no recorded completion criterion",
876
+ "supersedes": "",
855
877
  "text": match.group("text"),
856
878
  }
857
879
  )
858
880
  return items
859
881
 
860
882
 
861
- def _render_workitem_line(*, item_id: str, status: str, role: str, criterion: str, reason: str, text: str) -> str:
883
+ def _render_workitem_line(*, item_id: str, status: str, role: str, criterion: str, reason: str, supersedes: str, text: str) -> str:
862
884
  return (
863
885
  f"- [{_workitem_checkbox(status)}] {item_id} status=`{status}` role=`{role}` "
864
- f"criterion=`{criterion}` reason=`{reason}`: {text}"
886
+ f"criterion=`{criterion}` reason=`{reason}` supersedes=`{supersedes}`: {text}"
865
887
  )
866
888
 
867
889
 
@@ -913,6 +935,7 @@ def update_work_item(args) -> SkillRunResult:
913
935
  item_text = str(args.text or "").strip()
914
936
  criterion = str(getattr(args, "completion_criterion", None) or "").strip().replace("`", "'").replace("\n", " ")
915
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", " ")
916
939
  if not item_id:
917
940
  return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=["missing --item"])
918
941
  if status not in VALID_WORKITEM_STATUSES:
@@ -931,10 +954,21 @@ def update_work_item(args) -> SkillRunResult:
931
954
  items = _parse_work_items(text)
932
955
  existing = next((item for item in items if item["item_id"] == item_id), None)
933
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}"])
934
970
  existing["status"] = status
935
971
  existing["role"] = role
936
- if criterion:
937
- existing["criterion"] = criterion
938
972
  if reason:
939
973
  existing["reason"] = reason
940
974
  if item_text:
@@ -942,7 +976,9 @@ def update_work_item(args) -> SkillRunResult:
942
976
  else:
943
977
  if not item_text:
944
978
  return SkillRunResult(ok=False, skill_id=None, skill_doc=None, run_log=str(log_path), errors=["new work item requires --text"])
945
- items.append({"item_id": item_id, "status": status, "role": role, "criterion": criterion, "reason": reason, "text": item_text})
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})
946
982
 
947
983
  text = _replace_concrete_work_items_section(text, items)
948
984
  text = _append_phase_event(text, phase=f"workitem:{item_id}", status=status, role=role, note=item_text or None)
@@ -1469,6 +1505,10 @@ def _progression_record_errors(
1469
1505
  if not work_items:
1470
1506
  errors.append("at least one concrete work item is required before closure")
1471
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")
1472
1512
  if not item.get("criterion"):
1473
1513
  if item["status"] in {"unknown", "blocked", "escalated"} and item.get("reason"):
1474
1514
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xrefkit
3
- Version: 0.4.2
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
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