feat: complete evidence restructuring worktree

This commit is contained in:
Codex
2026-08-26 11:39:02 +02:00
parent a54d4769dd
commit 38f02cfd08
56 changed files with 1981 additions and 1801 deletions
+77 -3
View File
@@ -293,6 +293,15 @@ class EvidencePreparationReport:
model_calls: int
@dataclass(frozen=True)
class EvidenceMigrationReport:
"""Result of a deterministic Curated Evidence presentation-format migration."""
migrated: tuple[str, ...]
unchanged: tuple[str, ...]
findings: tuple[ValidationFinding, ...]
@dataclass(frozen=True)
class EvidenceResolutionReport:
"""The result of one curator-directed Evidence resolution."""
@@ -680,6 +689,48 @@ def prepare_workspace_evidence(
)
def migrate_workspace_evidence(
workspace_root: Path,
*,
git_status: Callable[[Path], tuple[str, ...]] | None = None,
) -> EvidenceMigrationReport:
"""Rewrite v1 Curated units as readable v2 Markdown without changing semantics."""
workspace_root = workspace_root.resolve()
evidence_root = workspace_root / "evidence"
_reject_dirty_authoring_state(workspace_root, git_status or _git_status)
try:
manifest = load_manifest(evidence_root / "manifest.yaml")
documents = load_curated_tree(evidence_root / "curated")
except (OSError, ValidationError, ValueError) as error:
raise EvidencePreparationError("authoring_state_invalid") from error
documents_by_id = {document.id: document for document in documents}
if len(documents_by_id) != len(documents):
raise EvidencePreparationError("duplicate_evidence_id")
migrated = tuple(sorted(
document.id for document in documents if document.schema_version == 1
))
unchanged = tuple(sorted(
document.id for document in documents if document.schema_version == 2
))
if not migrated:
return EvidenceMigrationReport(
migrated=(),
unchanged=unchanged,
findings=validate_workspace_evidence(workspace_root).findings,
)
upgraded = {
evidence_id: document.model_copy(update={"schema_version": 2})
for evidence_id, document in documents_by_id.items()
}
findings = _stage_and_apply_authoring_tree(workspace_root, upgraded, manifest)
return EvidenceMigrationReport(
migrated=migrated,
unchanged=unchanged,
findings=findings,
)
def resolve_workspace_evidence(
workspace_root: Path,
evidence_id: str,
@@ -827,7 +878,7 @@ def _load_resolution_source(evidence_root: Path, source_file: str) -> str:
def _git_status(workspace_root: Path) -> tuple[str, ...]:
result = subprocess.run(
["git", "status", "--porcelain"],
["git", "status", "--porcelain", "--untracked-files=all"],
cwd=workspace_root,
check=False,
capture_output=True,
@@ -835,7 +886,25 @@ def _git_status(workspace_root: Path) -> tuple[str, ...]:
)
if result.returncode != 0:
raise EvidencePreparationError("canonical_git_worktree_required")
return tuple(line for line in result.stdout.splitlines() if line)
prefix_result = subprocess.run(
["git", "rev-parse", "--show-prefix"],
cwd=workspace_root,
check=False,
capture_output=True,
text=True,
)
if prefix_result.returncode != 0:
raise EvidencePreparationError("canonical_git_worktree_required")
prefix = prefix_result.stdout.strip()
entries: list[str] = []
for line in result.stdout.splitlines():
if not line:
continue
path = line[3:] if len(line) > 3 else line
if prefix and path.startswith(prefix):
line = line[:3] + path.removeprefix(prefix)
entries.append(line)
return tuple(entries)
def _reject_dirty_authoring_state(
@@ -934,7 +1003,11 @@ def _candidate_to_evidence(
source_file: str,
source_hash: str,
) -> CuratedEvidence:
data = candidate.model_dump(mode="json", exclude={"existing_id", "supporting_excerpts"})
data = candidate.model_dump(
mode="json",
exclude={"schema_version", "existing_id", "supporting_excerpts"},
)
data["schema_version"] = 2
data["id"] = evidence_id
data["provenance"] = {
"source_file": source_file,
@@ -955,6 +1028,7 @@ def _unsupported_unit(
message="The current source no longer supports this Evidence unit.",
),)
return evidence.model_copy(update={
"schema_version": 2,
"provenance": evidence.provenance.model_copy(update={
"source_file": source_file,
"source_sha256": source_hash,