← Codex SecurityCONTENT HISTORY

Update to Codex Security

Snapshot Oct 7, 2026 · 06:02 UTC · version 0.1.32

WHAT CHANGED · RULE-BASED ANALYSIS

Package or technical metadata updated

Package contents changed in 77 files: .app.json, .codex-plugin/plugin.json, .mcp.json, …. Open the file diff to inspect the edits.

Observed in package metadata. These changes alone do not establish a new customer-facing feature.

Package file

Before

e4d5b22326ee380de5d779f7b5ba590c8d1bee9a80e8869fb2bd7f4def8e974d

After

a7bbe314059c7311e169474823e3978f267e560c3e961ba49fb94032da9843fe

Package file

Before

480

After

479

Package file

Before

3ddcc187fe2f1aff961bf55444e70bfbc3f825b6185ab3f29860d1a813a395f9

After

527d1a0db286884729572d400b0ad9b0ff852d4a12b36228ff5fbef180f7de9d

Package file

Before

40b84baf510169eadfd5f0fe0ad95001caedb7bc662d77c9ac2a4fbec7dd0845

After

7c5968060f6e848859ce02450d6e02e67029d8d39011bbda1059b19de6309ebf

Compare saved observations

Download comparison JSON

Changed files

.app.json →

.codex-plugin/plugin.json →

.mcp.json →

examples/completed-scan/report.md →

integrity.json →

mcp/helpers.mjs.br.part-000 →

mcp/native/THIRD_PARTY_NOTICES.txt →

mcp/native/darwin-arm64/unix.node →

mcp/native/darwin-x64/unix.node →

mcp/native/linux-arm64-gnu/unix.node →

mcp/native/linux-arm64-musl/unix.node →

mcp/native/linux-x64-gnu/unix.node →

mcp/native/linux-x64-musl/unix.node →

mcp/native/win32-arm64/windows.node →

mcp/native/win32-x64/windows.node →

mcp/server.mjs.br.part-000 →

mcp/server.mjs.br.part-001 →

references/artifact-storage.md →

references/config-preflight.md →

references/core-scan.md →

references/final-report.md →

references/scan-artifacts.md →

references/scan-contract.md →

references/threat-model.md →

schemas/coverage.schema.json →

schemas/scan-manifest.schema.json →

schemas/tools/scan-draft.schema.json →

scripts/config_preflight.py →

scripts/deep_scan_workbench.py →

scripts/filesystem_identity.py →

scripts/finalize_scan_contract.py →

scripts/finding_preview.py →

scripts/generate_in_scope_files.py →

scripts/generate_rank_input.py →

scripts/launch_codex_security_mcp →

scripts/normalize_candidates.py →

scripts/rank_preview.py →

scripts/report_projection.py →

scripts/snapshot_sqlite.py →

scripts/threat_model_projection.py →

scripts/windows_scan_local_files.py →

scripts/workbench/storage.py →

scripts/workbench_cli.py →

scripts/workbench_constants.py →

scripts/workbench_db.py →

scripts/workbench_finding_index.py →

scripts/workbench_native_indexes.py →

scripts/workbench_progress.py →

scripts/workbench_publication.py →

scripts/workbench_remediation.py →

scripts/workbench_saved_results.py →

scripts/workbench_scan_start.py →

scripts/workbench_scan_usage.py →

scripts/workbench_schema.py →

scripts/workbench_severity.py →

scripts/workbench_source_excerpt.py →

scripts/workbench_target.py →

scripts/workbench_target_state.py →

scripts/workbench_validation.py →

skills/assess-patch-risk/SKILL.md →

skills/assess-patch-risk/scripts/validate_patch_risk_assessment.py →

skills/attack-path-analysis/SKILL.md →

skills/deep-security-scan/SKILL.md →

skills/finding-discovery/SKILL.md →

skills/propose-security-hardening/references/proposal-format.md →

skills/security-diff-scan/SKILL.md →

skills/security-scan/SKILL.md →

skills/security-scan/references/desktop-scan.md →

skills/security-scan/references/scan-artifacts-and-ledger.md →

skills/threat-model/SKILL.md →

skills/track-findings/SKILL.md →

skills/track-findings/references/github-security-advisories.md →

skills/track-findings/references/jira.md →

skills/triage-finding/SKILL.md →

skills/triage-finding/agents/openai.yaml →

skills/triage-finding/references/ticket-intake.md →

skills/triage-finding/references/triage-result-contract.md →

scripts/workbench_saved_results.py

--- before
+++ after
@@ -12,10 +12,9 @@
 import sqlite3
 import stat
 import sys
-from collections.abc import Callable, Iterator
-from dataclasses import dataclass
+from collections.abc import Iterator
+from contextlib import contextmanager
 from pathlib import Path
-from types import ModuleType
 from typing import Any
 
 sys.path.insert(0, str(Path(__file__).resolve().parent))
@@ -25,18 +24,25 @@
     _populate_unsealed_artifact_envelope,
     _populate_unsealed_manifest_envelope,
     _prepare_scan_finalization,
+    _read_json,
+    _read_saved_threat_model,
     _read_scan_local_json,
     _read_scan_local_json_bytes,
+    _read_scan_local_json_with_metadata,
     _recover_unsealed_findings,
     _remove_scan_local_file_if_exists,
     _validate_completion_binding,
+    _validate_resolved_deferred,
+    _validate_schema_node,
     _write_prepared_scan_finalization,
     finalize_scan,
     finding_candidate_id,
     open_scan_local_file_descriptor,
     write_scan_local_bytes,
+    write_threat_model_projection_if_possible,
 )
 from workbench_constants import PHASES
+from workbench_target import committed_diff_snapshot_digest
 from workbench_validation import path_within_scope
 
 _PUBLISHED_OUTPUTS = (
@@ -44,6 +50,7 @@
     "coverage.json",
     "scan-manifest.json",
     "report.md",
+    "threatmodel.md",
     "report.html",
     "exports/results.sarif",
 )
@@ -55,29 +62,59 @@
 )
 
 
-@dataclass(frozen=True)
-class WorkbenchDbContext:
-    ARTIFACTS: dict[str, str]
-    artifact_path: Callable[..., Path | None]
-    deep_scan: ModuleType
-    expected_coverage_mode: Callable[..., str]
-    handoff: ModuleType
-    index_findings: Callable[..., None]
-    now: Callable[[], str]
-    optional_text: Callable[..., str | None]
-    parse_scan_cost: Callable[..., dict[str, Any] | None]
-    published_manifest_digest: Callable[..., str]
-    read_json_object: Callable[[Path], dict[str, Any]]
-    require_canonical_scan_directory: Callable[[Path], Path]
-    require_recorded_manifest_digest: Callable[..., None]
-    require_scan: Callable[..., Any]
-    require_uuid: Callable[[str, str], str]
-    require_workspace: Callable[..., Any]
-    scan_completion_lock: Callable[..., Any]
-    scan_context: Callable[..., dict[str, Any]]
-    verify_manifest_binding: Callable[..., None]
-    workbench_completion_binding: Callable[..., dict[str, Any]]
-    workspace_state: Callable[..., dict[str, Any]]
+def threat_model_fields(db: Any, scan: sqlite3.Row) -> dict[str, Any]:
+    scan_dir = Path(scan["scan_dir"])
+    fields: dict[str, Any] = {"threatModelAvailable": False}
+    try:
+        db.require_recorded_manifest_digest(scan, scan_dir)
+        db.verify_manifest_binding(
+            scan, _read_scan_local_json(scan_dir, db.ARTIFACTS["manifest"], "scan manifest")
+        )
+        saved_model = _read_saved_threat_model(scan_dir)
+        if saved_model is not None:
+            description = saved_model[0]
+            fields.update(
+                threatModelAvailable=True,
+                threatModelProvenance=description["provenance"],
+            )
+            if description["path"] is not None:
+                fields["threatModelPath"] = description["path"]
+    except (ContractError, OSError, SystemExit):
+        # Unavailable optional model data must not prevent reading the saved scan.
+        pass
+    return fields
+
+
+def refresh_completed_scan(
+    db: Any,
+    connection: sqlite3.Connection,
+    scan: sqlite3.Row,
+    cost_json: str | None,
+) -> dict[str, Any]:
+    warnings = json.loads(scan["completion_warnings_json"])
+    scan_dir = db.require_canonical_scan_directory(Path(scan["scan_dir"]))
+    db.require_recorded_manifest_digest(scan, scan_dir)
+    db.verify_manifest_binding(scan, db.read_json_object(scan_dir / db.ARTIFACTS["manifest"]))
+    try:
+        manifest, _, _ = finalize_scan(
+            scan_dir,
+            expected_coverage_mode=db.expected_coverage_mode(scan),
+            projection_warnings=warnings,
+        )
+    except ContractError as exc:
+        raise SystemExit(str(exc)) from exc
+    db.verify_manifest_binding(scan, manifest)
+    manifest_digest = db.published_manifest_digest(scan_dir, manifest)
+    db.pin_legacy_manifest_digest(connection, scan["id"], manifest_digest)
+    if cost_json is not None and scan["recipe_json"] is not None:
+        db.scan_usage.reconcile_completed_scan_cost(connection, scan, cost_json)
+    if warnings != json.loads(scan["completion_warnings_json"]):
+        with connection:
+            connection.execute(
+                "UPDATE scans SET completion_warnings_json = ? WHERE id = ?",
+                (json.dumps(warnings), scan["id"]),
+            )
+    return db.scan_context(connection, scan["id"])
 
 
 def _encoded(value: Any) -> bytes:
@@ -118,35 +155,51 @@
     )
 
 
-def _saved_result_paths(scan_dir: Path, workers: list[Any]) -> Iterator[tuple[str, str | None]]:
-    latest_reducer = _latest_successful_reducer(workers)
+def _checkpoint_paths(scan_dir: Path, directory: str) -> list[str]:
+    return [
+        f"{directory}/{name}"
+        for name in _children(scan_dir, directory)
+        if re.fullmatch(r"[0-9a-f]{64}\.json", name)
+    ]
 
-    def checkpoints(directory: str, kind: str | None = None) -> Iterator[tuple[str, str | None]]:
-        for name in _children(scan_dir, directory):
-            if re.fullmatch(r"[0-9a-f]{64}\.json", name):
-                yield f"{directory}/{name}", kind
 
-    yield from checkpoints("checkpoints")
+def _worker_outputs(scan_dir: Path, worker: Any) -> list[tuple[str, int]]:
+    output = Path(worker["artifact_dir"]).relative_to(scan_dir)
+    attempts = (output.parent if output.name == "output" else output) / "attempts"
+    archived = [
+        ((attempts / name).as_posix(), int(name.split("-")[1]))
+        for name in _children(scan_dir, attempts.as_posix())
+        if re.fullmatch(r"attempt-\d+", name)
+    ]
+    attempt = int(worker["attempt"] or 0) if "attempt" in worker.keys() else 0
+    if worker["kind"] == "discovery" and not attempt:
+        attempt = max((attempt for _, attempt in archived), default=0) + 1
+    return [(output.as_posix(), attempt), *archived]
+
+
+def _saved_result_paths(scan_dir: Path, workers: list[Any]) -> Iterator[tuple[str, str | None]]:
+    latest_reducer = _latest_successful_reducer(workers)
+    yield "checkpoint-head.json", None
+    for directory in ("checkpoint-heads", "checkpoints"):
+        yield from ((path, None) for path in _checkpoint_paths(scan_dir, directory))
     for worker in workers:
         if worker["kind"] not in {"dedup", "discovery"}:
             continue
         try:
-            output = Path(worker["artifact_dir"]).relative_to(scan_dir).as_posix()
+            outputs = _worker_outputs(scan_dir, worker)
         except (TypeError, ValueError):
             continue
-        attempts = (Path(output).parent if Path(output).name == "output" else Path(output)) / (
-            "attempts"
-        )
-        directories = [output] + [
-            (attempts / name).as_posix()
-            for name in _children(scan_dir, attempts.as_posix())
-            if re.fullmatch(r"attempt-\d+", name)
-        ]
-        for directory in directories:
-            checkpoint_paths = list(checkpoints(f"{directory}/checkpoints", worker["kind"]))
+        for directory, _ in outputs:
+            if worker["kind"] == "discovery":
+                yield f"{directory}/checkpoint-head.json", worker["kind"]
+                yield from (
+                    (path, worker["kind"])
+                    for path in _checkpoint_paths(scan_dir, f"{directory}/checkpoint-heads")
+                )
+            checkpoint_paths = _checkpoint_paths(scan_dir, f"{directory}/checkpoints")
             if worker["kind"] == "discovery" or checkpoint_paths:
                 yield f"{directory}/result.json", worker["kind"]
-                yield from checkpoint_paths
+                yield from ((path, worker["kind"]) for path in checkpoint_paths)
         if worker["result_manifest_path"] and (
             worker["kind"] == "discovery"
             or (latest_reducer is not None and worker["id"] == latest_reducer["id"])
@@ -160,17 +213,143 @@
                 continue
 
 
+def _checkpoint_head_directory(relative: str) -> Path | None:
+    path = Path(relative)
+    if path.name == "checkpoint-head.json":
+        return path.parent
+    if path.parent.name == "checkpoint-heads":
+        return path.parent.parent
+    return None
+
+
+def _capture_saved_source(
+    scan_dir: Path,
+    relative: str,
+    scan_id: str,
+    *,
+    kind: str | None = None,
+    snapshot_head: bool = True,
+    write: bool = True,
+) -> dict[str, tuple[str, int]]:
+    if not snapshot_head or Path(relative).name != "checkpoint-head.json":
+        _, digest, observed = _read_saved_result(scan_dir, relative, scan_id, kind=kind)
+        return {relative: (digest, observed)}
+    head, _, observed = _read_saved_result(scan_dir, relative, scan_id)
+    observation = {"checkpoint": head["checkpoint"], "observedAtNs": str(observed)}
+    directory = Path(relative).parent
+    selected = (directory / "checkpoints" / observation["checkpoint"]).as_posix()
+    _, selected_digest, selected_time = _read_saved_result(scan_dir, selected, scan_id)
+    digest = _digest(observation)
+    snapshot = (directory / "checkpoint-heads" / f"{digest}.json").as_posix()
+    # Capture the selected file even if the worker created it after directory enumeration.
+    if write and not (scan_dir / snapshot).exists():
+        write_scan_local_bytes(scan_dir, snapshot, _encoded(observation))
+    return {
+        snapshot: (digest, int(observation["observedAtNs"])),
+        selected: (selected_digest, selected_time),
+    }
+
+
+def _is_source_order_snapshot(relative: str) -> bool:
+    path = Path(relative)
+    return path.parent == Path("source-order") and bool(
+        re.fullmatch(r"[0-9a-f]{64}\.json", path.name)
+    )
+
+
 def _read_saved_result(
     scan_dir: Path, relative: str, scan_id: str, *, kind: str | None = None
-) -> tuple[dict[str, Any], str]:
-    draft = _read_scan_local_json(scan_dir, relative, "Saved scan checkpoint")
+) -> tuple[dict[str, Any], str, int]:
+    draft, _, metadata = _read_scan_local_json_with_metadata(
+        scan_dir, relative, "Saved scan checkpoint"
+    )
+    directory = _checkpoint_head_directory(relative)
+    if directory is not None:
+        checkpoint = draft.get("checkpoint")
+        if not isinstance(checkpoint, str) or not re.fullmatch(r"[0-9a-f]{64}\.json", checkpoint):
+            raise ContractError("checkpoint head does not name a saved checkpoint")
+        _read_saved_result(scan_dir, (directory / "checkpoints" / checkpoint).as_posix(), scan_id)
+        if Path(relative).name == "checkpoint-head.json":
+            return draft, _digest([draft, metadata.st_mtime_ns]), metadata.st_mtime_ns
+        observed = draft.get("observedAtNs")
+        if not isinstance(observed, str) or not re.fullmatch(r"-?[0-9]+", observed):
+            raise ContractError("checkpoint head has no observation time")
+        return draft, _digest(draft), int(observed)
     if draft.get("scanId") != scan_id:
         raise ContractError("checkpoint belongs to a different scan")
-    if not isinstance(draft.get("findings"), list) or not isinstance(
-        draft.get("coverage", {} if kind == "dedup" else None), dict
+    if not _is_source_order_snapshot(relative) and (
+        not isinstance(draft.get("findings"), list)
+        or not isinstance(draft.get("coverage", {} if kind == "dedup" else None), dict)
     ):
         raise ContractError("checkpoint has no semantic findings or coverage")
-    return draft, _digest(draft)
+    return draft, _digest(draft), metadata.st_mtime_ns
+
+
+def _frozen_source_times(scan_dir: Path, scan_id: str, sources: dict[str, str]) -> dict[str, int]:
+    times: dict[str, int] = {}
+    snapshots = [path for path in sources if _is_source_order_snapshot(path)]
+    for path in snapshots:
+        record, digest, _ = _read_saved_result(scan_dir, path, scan_id)
+        if digest != sources[path] or not isinstance(record.get("sources"), dict):
+            raise ContractError("saved source ordering changed after the scan stopped")
+        for relative, observation in record["sources"].items():
+            if (
+                not isinstance(observation, dict)
+                or relative not in sources
+                or observation.get("digest") != sources[relative]
+                or not isinstance(observation.get("observedAtNs"), str)
+                or not re.fullmatch(r"-?[0-9]+", observation["observedAtNs"])
+            ):
+                raise ContractError("saved source ordering does not match its frozen sources")
+            observed = int(observation["observedAtNs"])
+            if relative in times and times[relative] != observed:
+                raise ContractError("saved source ordering has conflicting observations")
+            times[relative] = observed
+    if snapshots and sources.keys() - set(snapshots) - times.keys():
+        raise ContractError("saved source ordering is incomplete")
+    return times
+
+
+def _freeze_source_times(
+    scan_dir: Path, scan_id: str, sources: dict[str, str], times: dict[str, int]
+) -> None:
+    # Identical result rewrites must not change the order of frozen review evidence.
+    observations = {
+        path: {"digest": digest, "observedAtNs": str(times[path])}
+        for path, digest in sources.items()
+        if not _is_source_order_snapshot(path)
+    }
+    if not observations:
+        return
+    record = {"scanId": scan_id, "sources": observations}
+    digest = _digest(record)
+    path = f"source-order/{digest}.json"
+    if not (scan_dir / path).exists():
+        write_scan_local_bytes(scan_dir, path, _encoded(record))
+    if _read_saved_result(scan_dir, path, scan_id)[1] != digest:
+        raise ContractError("saved source ordering does not match its digest")
+    sources[path] = digest
+
+
+def _parent_scan_draft(
+    scan_id: str,
+    parent_scan: dict[str, Any],
+    findings: dict[str, Any],
+    coverage: dict[str, Any],
+) -> dict[str, Any]:
+    parent = {
+        "scanId": scan_id,
+        "findings": findings.get("findings"),
+        "coverage": coverage,
+        **{
+            key: parent_scan[key]
+            for key in ("scope", "threatModel", "complete")
+            if key in parent_scan
+        },
+    }
+    if not isinstance(parent["findings"], list):
+        raise ContractError("Saved parent draft has no findings array")
+    return parent
 
 
 def _read_saved_parent_result(
@@ -188,29 +367,34 @@
         or coverage.get("scanId", scan_id) != scan_id
     ):
         raise ContractError("Saved parent documents belong to a different scan")
-    parent = {
-        "scanId": scan_id,
-        "findings": findings.get("findings"),
-        "coverage": coverage,
-        **{
-            key: parent_scan[key]
-            for key in ("scope", "threatModel", "complete")
-            if key in parent_scan
-        },
-    }
-    if not isinstance(parent["findings"], list):
-        raise ContractError("Saved parent draft has no findings array")
-    return manifest, parent
+    return manifest, _parent_scan_draft(scan_id, parent_scan, findings, coverage)
 
 
-def _source_digests(value: Any, label: str) -> dict[str, str]:
+def _source_digests(value: Any, error: str) -> dict[str, str]:
     if not isinstance(value, dict) or not all(
         isinstance(relative, str) and isinstance(digest, str) for relative, digest in value.items()
     ):
-        raise ContractError(f"{label} source digests are malformed.")
+        raise ContractError(error)
     return value
 
 
+def _retained_source_state(value: Any) -> tuple[dict[str, str], str | None]:
+    if isinstance(value, dict) and isinstance(value.get("sources"), dict):
+        sources = _source_digests(
+            value["sources"], "Saved stopped-scan source digests are malformed."
+        )
+        model_source = value.get("threatModelSource")
+        if not isinstance(model_source, str) or model_source not in sources:
+            raise ContractError("Saved stopped-scan model source is outside its checkpoint set.")
+        return sources, model_source
+    return _source_digests(value, "Saved stopped-scan source digests are malformed."), None
+
+
+def _encode_retained_sources(sources: dict[str, str], model_source: list[str]) -> str:
+    state = {"sources": sources, "threatModelSource": model_source[0]} if model_source else sources
+    return json.dumps(state, sort_keys=True)
+
+
 def _saved_results_changed(db: Any, connection: Any, scan: Any) -> bool:
     try:
         scan_dir = db.require_canonical_scan_directory(Path(scan["scan_dir"]))
@@ -234,7 +418,7 @@
 
         if manifest_path is None:
             if frozen_sources is not None:
-                return bool(_source_digests(json.loads(frozen_sources), "Frozen stopped-scan"))
+                return bool(_retained_source_state(json.loads(frozen_sources))[0])
             return has_saved_source()
         if scan["seal_manifest_digest"] is None:
             try:
@@ -252,14 +436,22 @@
         if not isinstance(manifest_scan, dict):
             return True
         published_sources = _source_digests(
-            manifest_scan.get("preservedSources", {}), "Published scan"
+            manifest_scan.get("preservedSources", {}),
+            "Published scan source digests are malformed.",
         )
         current_sources = dict(published_sources)
+        paths.update({path: None for path in published_sources if _is_source_order_snapshot(path)})
         for path in paths:
             try:
-                _, current_sources[path] = _read_saved_result(
-                    scan_dir, path, scan["id"], kind=paths[path]
+                captured = _capture_saved_source(
+                    scan_dir,
+                    path,
+                    scan["id"],
+                    kind=paths[path],
+                    snapshot_head=path not in published_sources,
+                    write=False,
                 )
+                current_sources.update({path: value[0] for path, value in captured.items()})
             except (ContractError, OSError, ValueError):
                 continue
         return current_sources != published_sources
@@ -273,7 +465,7 @@
     include_parent = True
     raw_frozen_sources = scan["retained_source_digests_json"]
     if raw_frozen_sources is not None:
-        frozen_sources = _source_digests(json.loads(raw_frozen_sources), "Saved stopped-scan")
+        frozen_sources, _ = _retained_source_state(json.loads(raw_frozen_sources))
         include_parent = False
 
     manifest_path = db.artifact_path(scan_dir, db.ARTIFACTS["manifest"], required=False)
@@ -291,7 +483,8 @@
         ):
             if "preservedSources" in manifest_scan:
                 published_sources = _source_digests(
-                    manifest_scan["preservedSources"], "Published scan"
+                    manifest_scan["preservedSources"],
+                    "Published scan source digests are malformed.",
                 )
                 include_parent = not published_sources
                 if published_sources:
@@ -312,21 +505,30 @@
     ).fetchall()
     paths = dict(_saved_result_paths(scan_dir, workers))
     recovery_sources = dict(frozen_sources or {})
+    source_times = _frozen_source_times(scan_dir, scan["id"], recovery_sources)
     for relative, expected_digest in recovery_sources.items():
         try:
-            _, digest = _read_saved_result(scan_dir, relative, scan["id"], kind=paths.get(relative))
+            _, digest, observed = _read_saved_result(
+                scan_dir, relative, scan["id"], kind=paths.get(relative)
+            )
         except (ContractError, OSError, ValueError) as exc:
             raise ContractError("Frozen stopped-scan checkpoint set is incomplete.") from exc
         if digest != expected_digest:
             raise ContractError("checkpoint changed after the scan stopped")
+        if not _is_source_order_snapshot(relative):
+            source_times.setdefault(relative, observed)
 
     for relative in paths.keys() - recovery_sources.keys():
         try:
-            _, recovery_sources[relative] = _read_saved_result(
-                scan_dir, relative, scan["id"], kind=paths[relative]
-            )
+            captured = _capture_saved_source(scan_dir, relative, scan["id"], kind=paths[relative])
         except (ContractError, OSError, ValueError):
             continue
+        for path, (digest, observed) in captured.items():
+            if path in recovery_sources and recovery_sources[path] != digest:
+                raise ContractError("checkpoint changed after the scan stopped")
+            recovery_sources[path] = digest
+            source_times.setdefault(path, observed)
+    _freeze_source_times(scan_dir, scan["id"], recovery_sources, source_times)
     return recovery_sources, include_parent
 
 
@@ -358,15 +560,10 @@
         else finding.get("identity")
     )
     if not isinstance(identity, dict):
-        extensions = finding.get("extensions")
-        source = str(
-            (extensions.get("candidateId") if isinstance(extensions, dict) else None)
-            or finding.get("title")
-            or "finding"
-        )
-        identity = {
-            "anchor": re.sub(r"[^a-z0-9._/-]+", "-", source.lower()).strip("._/-") or "finding"
-        }
+        normalized = dict(finding)
+        normalized.pop("identity", None)
+        _ensure_finding_identity(normalized)
+        identity = normalized["identity"]
     locations = finding.get("locations", [])
     if not isinstance(locations, list):
         locations = []
@@ -459,6 +656,257 @@
             )
 
 
+def _deferred_rows(coverage: dict[str, Any]) -> list[Any]:
+    rows = coverage.get("deferred", [])
+    return rows if isinstance(rows, list) else []
+
+
+def _resolved_deferred_rows(draft: dict[str, Any], schema: dict[str, Any]) -> list[dict[str, Any]]:
+    if draft.get("complete") is False:
+        return []
+    coverage = draft["coverage"]
+    rows = coverage.get("resolvedDeferred", [])
+    try:
+        # Invalid closure metadata cannot discard the evidence it names.
+        _validate_schema_node(rows, schema, "coverage.resolvedDeferred")
+        _validate_resolved_deferred({**coverage, "deferred": _deferred_rows(coverage)})
+    except ContractError:
+        return []
+    return rows
+
+
+def _merge_tied_parent_observations(
+    current: dict[str, Any], previous: dict[str, Any]
+) -> dict[str, Any]:
+    merged = copy.deepcopy(current)
+    for finding in previous["findings"]:
+        if finding not in merged["findings"]:
+            merged["findings"].append(copy.deepcopy(finding))
+    coverage = merged["coverage"]
+    for field in ("surfaces", "explicitExclusions", "deferred", "openQuestions"):
+        rows = previous["coverage"].get(field, [])
+        output = coverage.setdefault(field, [])
+        if isinstance(rows, list) and isinstance(output, list):
+            for row in rows:
+                if row not in output:
+                    output.append(copy.deepcopy(row))
+    pending_ids = {
+        identity
+        for row in _deferred_rows(coverage)
+        if isinstance(row, dict)
+        for identity in (row.get("id"), row.get("candidateId"))
+        if isinstance(identity, str)
+    }
+    closure_schema = _read_json(
+        Path(__file__).resolve().parent.parent / "schemas" / "coverage.schema.json"
+    )["properties"]["resolvedDeferred"]
+    closures = {}
+    for observed in (current, previous):
+        for row in _resolved_deferred_rows(observed, closure_schema):
+            if row["id"] not in pending_ids:
+                closures.setdefault(row["id"], copy.deepcopy(row))
+    coverage.pop("resolvedDeferred", None)
+    if closures:
+        coverage["resolvedDeferred"] = list(closures.values())
+    if current.get("complete") is False or previous.get("complete") is False:
+        merged["complete"] = False
+    if (
+        coverage.get("deferred")
+        or merged.get("complete") is False
+        or (
+            isinstance(coverage.get("surfaces"), list)
+            and any(
+                isinstance(row, dict) and row.get("disposition") == "needs_follow_up"
+                for row in coverage["surfaces"]
+            )
+        )
+        or previous["coverage"].get("completeness") == "partial"
+    ):
+        coverage["completeness"] = "partial"
+    return merged
+
+
+def _saved_coverage_id(item: dict[str, Any]) -> str:
+    return item.get("candidateId") or f"saved-{_digest(item)[:16]}"
+
+
+def _deferred_candidate_id(
+    row: dict[str, Any],
+    owner: str | None,
+    ambiguous_deferred: set[tuple[str | None, str]],
+) -> str | None:
+    identity = row.get("candidateId") or row.get("id")
+    if not isinstance(identity, str):
+        return None
+    if (owner, identity) in ambiguous_deferred and not any(
+        key in row for key in ("candidateId", "candidate", "finding")
+    ):
+        return None
+    return identity
+
+
+def _generic_surface_updates(
+    sources: list[tuple[str, dict[str, Any], str | None]],
+    source_order: dict[str, tuple[int, int]],
+    closed_deferred: dict[tuple[str | None, str], tuple[tuple[int, int], dict[str, Any], str]],
+    active_deferred: dict[tuple[str | None, str], tuple[tuple[int, int], dict[str, Any], str]],
+    resolved_candidates: dict[tuple[str | None, str], str],
+    reopened_generic: set[tuple[str | None, str]],
+    surface_schema: dict[str, Any],
+    deferred_rows: dict[str, list[Any]],
+    ambiguous_deferred: set[tuple[str | None, str]],
+) -> tuple[set[int], list[dict[str, Any]]]:
+    if not closed_deferred and not reopened_generic:
+        return set(), []
+
+    def linked(row: dict[str, Any], identity: str | None) -> bool:
+        surface_ids = row.get("surfaceIds", [])
+        return identity is not None and (
+            row.get("id") == identity or (isinstance(surface_ids, list) and identity in surface_ids)
+        )
+
+    saved_surfaces: dict[tuple[str | None, str], list[tuple[str, dict[str, Any]]]] = {}
+    for relative, draft, owner in sources:
+        rows = draft["coverage"].get("surfaces", [])
+        for row in rows if isinstance(rows, list) else []:
+            if isinstance(row, dict) and isinstance(row.get("id"), str):
+                saved_surfaces.setdefault((owner, row["id"]), []).append((relative, row))
+    replaced: set[int] = set()
+    updates: list[dict[str, Any]] = []
+    for relative, draft, owner in sources:
+        closed_ids = {
+            identity
+            for (saved_owner, identity), (_, _, source) in closed_deferred.items()
+            if saved_owner == owner and source == relative
+        }
+        reopened_ids = {
+            identity
+            for (saved_owner, identity), (_, _, source) in active_deferred.items()
+            if saved_owner == owner and source == relative and (owner, identity) in reopened_generic
+        }
+        if not closed_ids and not reopened_ids:
+            continue
+        current = draft["coverage"].get("surfaces", [])
+        for surface in current if isinstance(current, list) else []:
+            if not isinstance(surface, dict):
+                continue
+            reopening = surface.get("disposition") == "needs_follow_up"
+            work_ids = reopened_ids if reopening else closed_ids
+            if not work_ids:
+                continue
+            identity = surface.get("id")
+            if not isinstance(identity, str):
+                continue
+            matches = saved_surfaces[(owner, identity)]
+            # Older writers could assign one ID to distinct surfaces in a draft.
+            # A closure cannot identify which of those observations it replaces.
+            by_source = dict(matches)
+            if any(row != by_source[saved_path] for saved_path, row in matches):
+                continue
+            if any(
+                "candidateId" in row or "candidate" in row or "finding" in row for _, row in matches
+            ):
+                continue
+            if any(
+                row is not surface
+                and source_order[saved_path] >= source_order[relative]
+                and row.get("disposition") != surface.get("disposition")
+                for saved_path, row in matches
+            ):
+                continue
+
+            if not reopening and any(
+                saved_owner == owner
+                and (
+                    not isinstance(row.get("id") or row.get("candidateId"), str)
+                    or (
+                        isinstance(row.get("id"), str)
+                        and (owner, row["id"]) in ambiguous_deferred
+                        and not any(key in row for key in ("candidateId", "candidate", "finding"))
+                    )
+                )
+                and linked(row, identity)
+                for saved_path, _, saved_owner in sources
+                for row in deferred_rows[saved_path]
+                if isinstance(row, dict)
+            ):
+                continue
+            if not reopening and any(
+                saved_owner == owner
+                and (owner, deferred_id) not in closed_deferred
+                and (
+                    (candidate_id := _deferred_candidate_id(row, owner, ambiguous_deferred)) is None
+                    or (owner, candidate_id) not in resolved_candidates
+                )
+                and linked(row, identity)
+                for (saved_owner, deferred_id), (_, row, _) in active_deferred.items()
+            ):
+                continue
+            # An accepted checkpoint can update a saved surface by ID without
+            # optional surfaceIds links on its generic task.
+            latest_surface = max(matches, key=lambda match: source_order[match[0]])[1]
+            update = copy.deepcopy(latest_surface)
+            refs = update.setdefault("receiptRefs", [])
+            if isinstance(refs, list):
+                for _, row in matches:
+                    previous_refs = row.get("receiptRefs", [])
+                    for ref in previous_refs if isinstance(previous_refs, list) else []:
+                        if ref not in refs:
+                            refs.append(ref)
+            try:
+                _validate_schema_node(update, surface_schema, "coverage.surfaces")
+            except ContractError:
+                continue
+            replaced.update(
+                id(row)
+                for _, row in matches
+                if reopening
+                or row.get("disposition") in {"needs_follow_up", surface.get("disposition")}
+            )
+            if update not in updates:
+                updates.append(update)
+    return replaced, updates
+
+
+@contextmanager
+def preserve_parent_head_on_error(scan_dir: Path) -> Iterator[None]:
+    """Keep rejected completion attempts from becoming accepted parent observations."""
+    head_path = scan_dir / "checkpoint-head.json"
+    previous = None
+    try:
+        head_path.lstat()
+    except FileNotFoundError:
+        pass
+    else:
+        descriptor = open_scan_local_file_descriptor(
+            scan_dir, "checkpoint-head.json", "Saved parent checkpoint head"
+        )
+        with os.fdopen(descriptor, "rb") as handle:
+            metadata = os.fstat(handle.fileno())
+            previous = (handle.read(), metadata)
+    directories = ("checkpoints", "checkpoint-heads")
+    previous_files = {
+        relative for directory in directories for relative in _checkpoint_paths(scan_dir, directory)
+    }
+    try:
+        yield
+    except ContractError:
+        if previous is None:
+            _remove_scan_local_file_if_exists(scan_dir, "checkpoint-head.json")
+        else:
+            payload, metadata = previous
+            write_scan_local_bytes(scan_dir, "checkpoint-head.json", payload)
+            os.utime(head_path, ns=(metadata.st_atime_ns, metadata.st_mtime_ns))
+        current_files = {
+            relative
+            for directory in directories
+            for relative in _checkpoint_paths(scan_dir, directory)
+        }
+        for relative in current_files - previous_files:
+            _remove_scan_local_file_if_exists(scan_dir, relative)
+        raise
+
+
 def merge_saved_results(
     scan_dir: Path,
     scan_id: str,
@@ -470,14 +918,25 @@
     reason: str,
     frozen_source_digests: dict[str, str] | None = None,
     allow_frozen_legacy_parent: bool = False,
+    frozen_model_source: str | None = None,
+    selected_model_source: list[str] | None = None,
 ) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]] | None:
     """Read only bound parent/worker files; return an unsealed loss-preserving union."""
     initial_warnings = set(warnings)
+    try:
+        source_times = _frozen_source_times(scan_dir, scan_id, frozen_source_digests or {})
+    except (ContractError, OSError, ValueError) as exc:
+        raise ContractError("Frozen stopped-scan checkpoint set is incomplete.") from exc
     parent: dict[str, Any] | None = None
     parent_manifest: dict[str, Any] | None = None
+    parent_is_canonical = False
+    parent_modified = 0
     if frozen_source_digests is None or allow_frozen_legacy_parent:
         try:
             parent_manifest, parent = _read_saved_parent_result(scan_dir, scan_id)
+            # Without an accepted head, file-authored coverage is a full replacement.
+            parent_modified = (scan_dir / "coverage.json").lstat().st_mtime_ns
+            parent_is_canonical = True
         except (ContractError, OSError, ValueError) as exc:
             if not stopped:
                 raise
@@ -487,25 +946,66 @@
             parent = None
         if parent_manifest is not None and parent is not None:
             parent_scan = parent_manifest["scan"]
+            try:
+                previous_head, _, head_modified = _read_saved_result(
+                    scan_dir, "checkpoint-head.json", scan_id
+                )
+            except (ContractError, OSError, ValueError):
+                head_modified = None
+            if head_modified is not None:
+                # A partial tool publication must not outrank its accepted head.
+                parent_modified = min(
+                    (scan_dir / name).lstat().st_mtime_ns
+                    for name in ("findings.json", "coverage.json", "scan-manifest.json")
+                )
             if not parent_scan.get("sealedAt") or allow_frozen_legacy_parent:
+                head_path = scan_dir / "checkpoint-head.json"
+                tied_observations = False
+                if head_modified == parent_modified:
+                    previous_parent, _, _ = _read_saved_result(
+                        scan_dir, f"checkpoints/{previous_head['checkpoint']}", scan_id
+                    )
+                    if previous_parent != parent:
+                        # Tied observations cannot decide which pending work came last.
+                        parent = _merge_tied_parent_observations(parent, previous_parent)
+                        parent_is_canonical = False
+                        tied_observations = True
                 payload = _encoded(parent)
                 parent_digest = hashlib.sha256(payload).hexdigest()
                 parent_checkpoint = f"checkpoints/{parent_digest}.json"
-                write_scan_local_bytes(scan_dir, parent_checkpoint, payload)
+                checkpoint_path = scan_dir / parent_checkpoint
+                if not checkpoint_path.exists():
+                    write_scan_local_bytes(scan_dir, parent_checkpoint, payload)
+                    # A recovery copy must not appear newer than the review it copies.
+                    os.utime(checkpoint_path, ns=(parent_modified, parent_modified))
+                if head_modified is None or head_modified < parent_modified or tied_observations:
+                    write_scan_local_bytes(
+                        scan_dir,
+                        "checkpoint-head.json",
+                        _encoded({"checkpoint": checkpoint_path.name}),
+                    )
+                    os.utime(head_path, ns=(parent_modified, parent_modified))
                 if frozen_source_digests is not None:
+                    captured = _capture_saved_source(scan_dir, "checkpoint-head.json", scan_id)
                     frozen_source_digests = {
                         **frozen_source_digests,
                         parent_checkpoint: parent_digest,
+                        **{path: value[0] for path, value in captured.items()},
                     }
 
     sources: list[tuple[str, dict[str, Any], str | None]] = []
     parent_preserved_sources: dict[str, str] = {}
     source_digests: dict[str, str] = {}
+    source_order: dict[str, tuple[int, int]] = {}
+    worker_attempts: dict[str, tuple[str, int]] = {}
+    saved_heads: dict[str, str] = {}
     if parent_manifest:
         recorded = parent_manifest["scan"].get("preservedSources", {})
         if isinstance(recorded, dict):
             parent_preserved_sources = recorded
             source_digests.update(parent_preserved_sources)
+            if frozen_source_digests is None:
+                source_times.update(_frozen_source_times(scan_dir, scan_id, recorded))
     paths: dict[str, str | None] = {}
     reducer_paths: set[str] = set()
     current_results: set[str] = set()
@@ -520,57 +1020,43 @@
         except ValueError:
             warnings.append("Skipped a reducer result outside the scan directory.")
 
-    def checkpoints(directory: str, worker_id: str | None) -> None:
-        for name in _children(scan_dir, directory):
-            if re.fullmatch(r"[0-9a-f]{64}\.json", name):
-                paths[f"{directory}/{name}"] = worker_id
+    def checkpoints(directory: str, worker_id: str | None, attempt: int = 0) -> None:
+        if worker_id is not None:
+            root = Path(directory).parent.as_posix()
+            worker_attempts[root] = (worker_id, attempt)
+            paths[f"{root}/checkpoint-head.json"] = worker_id
+        head_snapshots = (Path(directory).parent / "checkpoint-heads").as_posix()
+        for saved_directory in (directory, head_snapshots):
+            paths.update(dict.fromkeys(_checkpoint_paths(scan_dir, saved_directory), worker_id))
 
+    paths["checkpoint-head.json"] = None
     checkpoints("checkpoints", None)
     for worker in workers:
         try:
-            output = Path(worker["artifact_dir"]).relative_to(scan_dir).as_posix()
+            outputs = _worker_outputs(scan_dir, worker)
         except (TypeError, ValueError):
             warnings.append("Skipped a worker checkpoint outside the scan directory.")
             continue
         if worker["kind"] == "dedup":
-
-            def reducer_output(directory: str, attempt: int, reducer_worker: Any) -> None:
-                result_path = f"{directory}/result.json"
-                checkpoint_paths = [
-                    f"{directory}/checkpoints/{name}"
-                    for name in _children(scan_dir, f"{directory}/checkpoints")
-                    if re.fullmatch(r"[0-9a-f]{64}\.json", name)
-                ]
+            for directory, attempt in outputs:
+                checkpoint_paths = _checkpoint_paths(scan_dir, f"{directory}/checkpoints")
                 if not checkpoint_paths:
-                    return
-                paths[result_path] = None
-                for checkpoint_path in checkpoint_paths:
-                    paths[checkpoint_path] = None
-                reducer_paths.update([result_path, *checkpoint_paths])
-                reducer_outputs.append((reducer_worker, result_path, checkpoint_paths, attempt))
-
-            reducer_output(output, int(worker["attempt"] or 0), worker)
-            attempts = (
-                Path(output).parent if Path(output).name == "output" else Path(output)
-            ) / "attempts"
-            for name in _children(scan_dir, attempts.as_posix()):
-                match = re.fullmatch(r"attempt-(\d+)", name)
-                if match:
-                    reducer_output((attempts / name).as_posix(), int(match.group(1)), worker)
+                    continue
+                result_path = f"{directory}/result.json"
+                retained_paths = [result_path, *checkpoint_paths]
+                paths.update(dict.fromkeys(retained_paths))
+                reducer_paths.update(retained_paths)
+                reducer_outputs.append((worker, result_path, checkpoint_paths, attempt))
             continue
         if worker["kind"] != "discovery":
             continue
+        output, attempt = outputs[0]
         paths[f"{output}/result.json"] = worker["id"]
         current_results.add(f"{output}/result.json")
-        checkpoints(f"{output}/checkpoints", worker["id"])
-        attempts = (
-            Path(output).parent if Path(output).name == "output" else Path(output)
-        ) / "attempts"
-        for name in _children(scan_dir, attempts.as_posix()):
-            if re.fullmatch(r"attempt-\d+", name):
-                archived = (attempts / name).as_posix()
-                paths[f"{archived}/result.json"] = worker["id"]
-                checkpoints(f"{archived}/checkpoints", worker["id"])
+        for archived, archived_attempt in outputs[1:]:
+            paths[f"{archived}/result.json"] = worker["id"]
+            checkpoints(f"{archived}/checkpoints", worker["id"], archived_attempt)
+        checkpoints(f"{output}/checkpoints", worker["id"], attempt)
         if worker["result_manifest_path"]:
             try:
                 current_path = Path(worker["result_manifest_path"]).relative_to(scan_dir).as_posix()
@@ -579,7 +1065,26 @@
             except ValueError:
                 warnings.append("Skipped a worker result outside the scan directory.")
 
+    if frozen_source_digests is None:
+        for relative, worker_id in list(paths.items()):
+            if Path(relative).name != "checkpoint-head.json":
+                continue
+            del paths[relative]
+            try:
+                captured = _capture_saved_source(scan_dir, relative, scan_id)
+                paths.update({path: worker_id for path in captured})
+            except (ContractError, OSError, ValueError) as exc:
+                if (scan_dir / relative).exists():
+                    warnings.append(f"Preserved unreadable checkpoint {relative}: {exc}")
+
     if frozen_source_digests is not None:
+        source_digests.update(
+            {
+                path: digest
+                for path, digest in frozen_source_digests.items()
+                if _is_source_order_snapshot(path)
+            }
+        )
         paths = {
             relative: worker_id
             for relative, worker_id in paths.items()
@@ -591,12 +1096,17 @@
 
     for relative, worker_id in paths.items():
         try:
-            draft, digest = _read_saved_result(
+            draft, digest, observed = _read_saved_result(
                 scan_dir, relative, scan_id, kind="dedup" if relative in reducer_paths else None
             )
             if frozen_source_digests is not None and frozen_source_digests[relative] != digest:
                 raise ContractError("checkpoint changed after the scan stopped")
             source_digests[relative] = digest
+            source_times.setdefault(relative, observed)
+            source_order[relative] = (0, source_times[relative])
+            if _checkpoint_head_directory(relative) is not None:
+                saved_heads[relative] = draft["checkpoint"]
+                continue
             # Recovery expects coverage, but reducer results only contain findings
             # and context. Add an empty value after hashing the original result.
             sources.append((relative, {"coverage": {}, **draft}, worker_id))
@@ -607,6 +1117,37 @@
         if frozen_source_digests.keys() - source_digests.keys():
             raise ContractError("Frozen stopped-scan checkpoint set is incomplete.")
 
+    # Frozen observations retain checkpoint selection even if a worker moves its head.
+    headed_workers = {
+        worker_attempts[_checkpoint_head_directory(head).as_posix()][0]
+        for head in saved_heads
+        if _checkpoint_head_directory(head) != Path(".")
+    }
+    for relative, _, worker_id in sources:
+        if worker_id not in headed_workers:
+            continue
+        directory = Path(relative).parent
+        if directory.name == "checkpoints":
+            directory = directory.parent
+        _, attempt = worker_attempts.get(directory.as_posix(), (worker_id, 0))
+        source_order[relative] = (attempt, source_order[relative][1])
+    selected_observations: dict[str, tuple[int, int]] = {}
+    parent_heads: list[tuple[int, str]] = []
+    for head, checkpoint in saved_heads.items():
+        directory = _checkpoint_head_directory(head)
+        selected = (directory / "checkpoints" / checkpoint).as_posix()
+        if selected not in source_order:
+            raise ContractError("Checkpoint head is outside the saved source set.")
+        observed = source_order[head][1]
+        if directory == Path("."):
+            order = (0, max(source_order[selected][1], observed))
+            parent_heads.append((observed, selected))
+        else:
+            _, attempt = worker_attempts[directory.as_posix()]
+            order = (attempt, observed)
+        selected_observations[selected] = max(selected_observations.get(selected, order), order)
+    source_order.update(selected_observations)
+
     drafts_by_path = {relative: draft for relative, draft, _ in sources}
     latest_reducer_key = (
         (reducer["completed_at"] or "", reducer["id"], int(reducer["attempt"] or 0))
@@ -627,8 +1168,67 @@
             latest_reducer_key = candidate_key
             latest_reducer = result_path
 
+    if parent_heads:
+        latest_observation = max(observed for observed, _ in parent_heads)
+        for observed, parent_path in parent_heads:
+            if observed != latest_observation:
+                continue
+            draft = drafts_by_path[parent_path]
+            modified = source_order[parent_path][1]
+            if parent is None or modified > parent_modified:
+                parent = draft
+                parent_modified = modified
+                parent_is_canonical = False
+            elif modified == parent_modified and draft != parent:
+                parent = _merge_tied_parent_observations(parent, draft)
+                parent_is_canonical = False
     if parent is None and latest_reducer is not None:
-        parent = next((draft for relative, draft, _ in sources if relative == latest_reducer), None)
+        parent = drafts_by_path[latest_reducer]
+
+    all_sources = ([("parent", parent, None)] if parent else []) + sources
+    # Older checkpoints can omit IDs already assigned in their published output.
+    for field in ("deferred", "surfaces"):
+        named_rows: dict[str | None, list[dict[str, Any]]] = {}
+        for _, draft, owner in all_sources:
+            rows = draft["coverage"].get(field, [])
+            named_rows.setdefault(owner, []).extend(
+                row
+                for row in (rows if isinstance(rows, list) else [])
+                if isinstance(row, dict) and isinstance(row.get("id"), str)
+            )
+        for _, draft, owner in all_sources:
+            rows = draft["coverage"].get(field, [])
+            if not isinstance(rows, list):
+                continue
+            reserved = {
+                row["id"]
+                for row in rows
+                if isinstance(row, dict) and isinstance(row.get("id"), str)
+            }
+            for row in rows:
+                if not isinstance(row, dict) or "id" in row:
+                    continue
+                if field == "deferred" and any(
+                    key in row for key in ("candidateId", "candidate", "finding")
+                ):
+                    continue
+                content = {"receiptRefs": [], **row} if field == "surfaces" else row
+                identity = next(
+                    (
+                        named["id"]
+                        for named in named_rows[owner]
+                        if named["id"] not in reserved
+                        and {
+                            **({"receiptRefs": []} if field == "surfaces" else {}),
+                            **{key: value for key, value in named.items() if key != "id"},
+                        }
+                        == content
+                    ),
+                    None,
+                )
+                if identity is not None:
+                    row["id"] = identity
+                    reserved.add(identity)
 
     if parent is None and not sources:
         return None
@@ -641,6 +1241,13 @@
     ):
         return None
 
+    if (
+        frozen_source_digests is None
+        or any(_is_source_order_snapshot(path) for path in source_digests)
+        or allow_frozen_legacy_parent
+    ):
+        _freeze_source_times(scan_dir, scan_id, source_digests, source_times)
+
     target_kind = binding["allowedTargetKinds"][0]
     if (
         target_kind == "git_worktree"
@@ -651,15 +1258,9 @@
     target = {"kind": target_kind, **binding["target"]}
     if target["kind"] == "git_diff" and "snapshotDigest" not in target:
         diff_kind = {"commit": "commit", "branch_diff": "range"}[binding["coverageMode"]]
-        digest = hashlib.sha256(
-            b"codex-security-diff/v1\0"
-            + diff_kind.encode()
-            + b"\0"
-            + target["baseRevision"].encode()
-            + b"\0"
-            + target["headRevision"].encode()
-        ).hexdigest()
-        target["snapshotDigest"] = f"codex-security-snapshot/v1:sha256:{digest}"
+        target["snapshotDigest"] = committed_diff_snapshot_digest(
+            diff_kind, target["baseRevision"], target["headRevision"]
+        )
     manifest = (
         copy.deepcopy(parent_manifest)
         if parent_manifest
@@ -668,6 +1269,27 @@
     for key in ("sealedAt", "artifacts"):
         manifest["scan"].pop(key, None)
     manifest["scan"]["preservedSources"] = source_digests
+    # Completion follows the selected parent, including legacy terminal drafts
+    # that omit the optional marker, rather than an older canonical manifest.
+    if parent is not None:
+        manifest["scan"].pop("complete", None)
+        if "complete" in parent:
+            manifest["scan"]["complete"] = parent["complete"]
+        for key in ("scope", "threatModel"):
+            if isinstance(parent.get(key), dict):
+                manifest["scan"][key] = copy.deepcopy(parent[key])
+        if isinstance(manifest["scan"].get("scope"), dict):
+            manifest["scan"]["scope"].update(copy.deepcopy(binding["scope"]))
+    if frozen_model_source is not None:
+        # Publication retries retain the choice made with the frozen source set.
+        model = drafts_by_path.get(frozen_model_source, {}).get("threatModel")
+        if not isinstance(model, dict):
+            raise ContractError("Frozen stopped-scan model source is unavailable.")
+        manifest["scan"]["threatModel"] = copy.deepcopy(model)
+        if paths[frozen_model_source] is not None:
+            manifest["scan"]["threatModel"]["origin"] = "recovered"
+        if selected_model_source is not None:
+            selected_model_source[:] = [frozen_model_source]
     coverage = (
         copy.deepcopy(parent["coverage"])
         if parent and parent["coverage"]
@@ -696,7 +1318,7 @@
             for field in ("surfaces", "explicitExclusions", "deferred")
             for item in (coverage.get(field) if isinstance(coverage.get(field), list) else [])
         }
-        if parent_manifest
+        if parent_is_canonical
         else set()
     )
     findings: list[dict[str, Any]] = []
@@ -725,32 +1347,180 @@
         )
         return bool(document["findings"])
 
-    all_sources = ([("parent", parent, None)] if parent else []) + sources
-    current_drafts = ([(None, parent)] if parent else []) + [
-        (worker_id, draft) for relative, draft, worker_id in sources if relative in current_results
+    source_order["parent"] = (0, parent_modified)
+    deferred_rows = {
+        relative: _deferred_rows(draft["coverage"]) for relative, draft, _ in all_sources
+    }
+    # A legacy source can contain independent tasks with the same explicit ID.
+    # Later rewrites cannot make an ID-only closure identify one of those tasks.
+    ambiguous_deferred: set[tuple[str | None, str]] = set()
+    for relative, _, owner in all_sources:
+        by_id: dict[str, dict[str, Any]] = {}
+        candidate_aliases = {
+            identity
+            for row in deferred_rows[relative]
+            if isinstance(row, dict)
+            and any(key in row for key in ("candidateId", "candidate", "finding"))
+            for identity in (row.get("id"), row.get("candidateId"))
+            if isinstance(identity, str)
+        }
+        for row in deferred_rows[relative]:
+            if not isinstance(row, dict) or not isinstance(identity := row.get("id"), str):
+                continue
+            if any(key in row for key in ("candidateId", "candidate", "finding")):
+                continue
+            if identity in candidate_aliases or (identity in by_id and row != by_id[identity]):
+                ambiguous_deferred.add((owner, identity))
+            by_id[identity] = row
+    current_drafts = ([("parent", parent, None)] if parent else []) + [
+        source for source in sources if source[0] in current_results | selected_observations.keys()
+    ]
+    # Generic closures belong to one logical scan or worker, just like candidates.
+    # Keep them when recovering a terminal checkpoint without its canonical write.
+    closed_deferred: dict[tuple[str | None, str], tuple[tuple[int, int], dict[str, Any], str]] = {}
+
+    candidate_ids: set[tuple[str | None, str]] = set()
+    accepted_deferred_orders: dict[tuple[str | None, str], tuple[int, int]] = {}
+    active_deferred: dict[tuple[str | None, str], tuple[tuple[int, int], dict[str, Any], str]] = {}
+    coverage_schema = _read_json(
+        Path(__file__).resolve().parent.parent / "schemas" / "coverage.schema.json"
+    )["properties"]
+    for relative, draft, owner in all_sources:
+        order = source_order[relative]
+        for item in deferred_rows[relative]:
+            if not isinstance(item, dict):
+                continue
+            if isinstance(item.get("candidateId"), str) or "candidate" in item or "finding" in item:
+                candidate_ids.update(
+                    (owner, identity)
+                    for identity in (item.get("id"), item.get("candidateId"))
+                    if isinstance(identity, str)
+                )
+            identity = item.get("id") or item.get("candidateId")
+            if isinstance(identity, str):
+                key = (owner, identity)
+                previous = active_deferred.get(key)
+                if previous is None or order > previous[0]:
+                    active_deferred[key] = (order, item, relative)
+                if (
+                    relative == "parent"
+                    or relative in current_results
+                    or relative in selected_observations
+                ):
+                    try:
+                        # A malformed update cannot discard valid saved evidence.
+                        _validate_schema_node(
+                            item, coverage_schema["deferred"]["items"], "coverage.deferred"
+                        )
+                    except ContractError:
+                        continue
+                    accepted_deferred_orders[key] = max(
+                        accepted_deferred_orders.get(key, order), order
+                    )
+        for closure in _resolved_deferred_rows(draft, coverage_schema["resolvedDeferred"]):
+            key = (owner, closure["id"])
+            previous = closed_deferred.get(key)
+            if previous is None or order > previous[0]:
+                # The parent comes first, preserving its reason on equal timestamps.
+                closed_deferred[key] = (order, closure, relative)
+    reopened_rows: list[tuple[str | None, dict[str, Any]]] = []
+    reopened_generic: set[tuple[str | None, str]] = set()
+    for key, (order, _, _) in list(closed_deferred.items()):
+        # Equal timestamps cannot distinguish closure from reopened work.
+        reopened = [
+            active
+            for (owner, identity), active in active_deferred.items()
+            if owner == key[0]
+            and (identity == key[1] or active[1].get("candidateId") == key[1])
+            and active[0] >= order
+        ]
+        if key in candidate_ids or key in ambiguous_deferred or reopened:
+            del closed_deferred[key]
+        for _, item, _ in reopened:
+            reopened_rows.append((key[0], item))
+            if key not in candidate_ids:
+                reopened_generic.add(key)
+    # Retain every distinct ambiguous task even when a terminal result supersedes
+    # its source. Existing output ID normalization keeps each row distinct.
+    for relative, _, owner in all_sources:
+        for row in deferred_rows[relative]:
+            if (
+                isinstance(row, dict)
+                and isinstance(identity := row.get("id"), str)
+                and (owner, identity) in ambiguous_deferred
+                and not any(key in row for key in ("candidateId", "candidate", "finding"))
+                and (owner, row) not in reopened_rows
+            ):
+                reopened_rows.append((owner, row))
+    parent_closures = [
+        closure for (owner, _), (_, closure, _) in closed_deferred.items() if owner is None
     ]
+    coverage.pop("resolvedDeferred", None)
+    if parent_closures:
+        coverage["resolvedDeferred"] = copy.deepcopy(parent_closures)
+        if parent:
+            coverage["deferred"] = [
+                row
+                for row in deferred_rows["parent"]
+                if not isinstance(row, dict)
+                or not isinstance(row.get("id"), str)
+                or (None, row["id"]) not in closed_deferred
+            ]
     resolved: dict[tuple[str | None, str], str] = {}
-    for owner, draft in current_drafts:
+
+    ordered_candidates = {
+        (owner, identity)
+        for owner, row in reopened_rows
+        if (identity := _deferred_candidate_id(row, owner, ambiguous_deferred)) is not None
+        and (owner, identity) in candidate_ids
+    }
+    ordered_outcomes: dict[tuple[str | None, str], tuple[tuple[int, int], str]] = {}
+
+    outcomes: list[tuple[str, str | None, str, str]] = []
+    for relative, draft, owner in current_drafts:
         for finding in draft["findings"]:
             if (
                 isinstance(finding, dict)
                 and valid_finding(finding)
                 and (candidate_id := finding_candidate_id(finding))
             ):
-                resolved.setdefault((owner, candidate_id), "reported")
+                outcomes.append((relative, owner, candidate_id, "reported"))
         for field in ("surfaces", "explicitExclusions"):
             items = draft["coverage"].get(field, [])
             for item in items if isinstance(items, list) else []:
                 if (
                     isinstance(item, dict)
                     and isinstance(item.get("candidateId"), str)
-                    and item.get("disposition") in {"reported", "rejected", "not_applicable"}
+                    and item.get("disposition") in {"rejected", "not_applicable"}
                 ):
-                    resolved.setdefault((owner, item["candidateId"]), item["disposition"])
+                    outcomes.append((relative, owner, item["candidateId"], item["disposition"]))
+    ordered_candidates.update(
+        (owner, candidate_id)
+        for relative, owner, candidate_id, _ in outcomes
+        if owner is not None and relative in selected_observations
+    )
+    # Reopened work and selected checkpoint outcomes follow the saved source order.
+    for relative, owner, candidate_id, disposition in outcomes:
+        key = (owner, candidate_id)
+        if key not in ordered_candidates:
+            if relative == "parent" or relative in current_results:
+                resolved.setdefault(key, disposition)
+            continue
+        order = source_order[relative]
+        if any(
+            saved_owner == owner
+            and _deferred_candidate_id(row, owner, ambiguous_deferred) == candidate_id
+            and modified >= order
+            for (saved_owner, _), (modified, row, _) in active_deferred.items()
+        ):
+            continue
+        if key not in ordered_outcomes or order > ordered_outcomes[key][0]:
+            resolved[key] = disposition
+            ordered_outcomes[key] = (order, relative)
     # Only the current parent may claim that another worker finding was absorbed.
     # A superseded checkpoint must not suppress a newer independent result.
-    for draft in [parent] if parent else []:
-        for finding in draft["findings"]:
+    if parent:
+        for finding in parent["findings"]:
             if valid_finding(finding):
                 canonical_key = _finding_key(finding)
                 for retained in _retained_findings(finding):
@@ -787,26 +1557,155 @@
                             represented_candidate_history.setdefault(candidate_key, set()).add(
                                 _digest(_finding_content(original["finding"]))
                             )
-                            resolved.setdefault(candidate_key, "reported")
+    replaced_surfaces, surface_updates = _generic_surface_updates(
+        all_sources,
+        source_order,
+        closed_deferred,
+        active_deferred,
+        resolved,
+        reopened_generic,
+        coverage_schema["surfaces"]["items"],
+        deferred_rows,
+        ambiguous_deferred,
+    )
+    replaced_rows = {
+        "surfaces": replaced_surfaces,
+        "deferred": {
+            id(row)
+            for relative, _, owner in all_sources
+            for row in deferred_rows[relative]
+            if isinstance(row, dict)
+            and isinstance(identity := row.get("id"), str)
+            and (key := (owner, identity)) not in candidate_ids
+            and key not in ambiguous_deferred
+            and (updated := accepted_deferred_orders.get(key)) is not None
+            and updated > source_order[relative]
+        },
+    }
+    for field, replaced in replaced_rows.items():
+        if parent and isinstance(coverage.get(field), list):
+            previous = parent["coverage"].get(field, [])
+            if isinstance(previous, list):
+                removed_parent = [row for row in previous if id(row) in replaced]
+                coverage[field] = [row for row in coverage[field] if row not in removed_parent]
+    if isinstance(coverage.get("surfaces"), list):
+        for surface in surface_updates:
+            if surface not in coverage["surfaces"]:
+                coverage["surfaces"].append(surface)
+        for surface in coverage["surfaces"]:
+            if isinstance(surface, dict):
+                surface.setdefault("receiptRefs", [])
+
+    # Reopened work survives a superseded checkpoint, but current candidate
+    # outcomes still apply. Parent closures cannot remove another worker's row.
+    for owner, item in reopened_rows:
+        if (identity := _deferred_candidate_id(item, owner, ambiguous_deferred)) is not None and (
+            owner,
+            identity,
+        ) in resolved:
+            continue
+        pending = coverage.setdefault("deferred", [])
+        if isinstance(pending, list) and item not in pending:
+            pending.append(copy.deepcopy(item))
+    ambiguous_surface_ids = {
+        (owner, identity)
+        for owner, row in reopened_rows
+        if isinstance(row.get("id"), str)
+        and (owner, row["id"]) in ambiguous_deferred
+        and not any(key in row for key in ("candidateId", "candidate", "finding"))
+        for identity in [
+            row["id"],
+            *(row.get("surfaceIds", []) if isinstance(row.get("surfaceIds", []), list) else []),
+        ]
+        if isinstance(identity, str)
+    }
+    for _, draft, owner in all_sources:
+        surfaces = draft["coverage"].get("surfaces", [])
+        by_id: dict[str, dict[str, Any]] = {}
+        for surface in surfaces if isinstance(surfaces, list) else []:
+            if not isinstance(surface, dict) or not isinstance(identity := surface.get("id"), str):
+                continue
+            if identity in by_id and surface != by_id[identity]:
+                ambiguous_surface_ids.add((owner, identity))
+            by_id[identity] = surface
+    for _, draft, owner in all_sources:
+        surfaces = draft["coverage"].get("surfaces", [])
+        for surface in surfaces if isinstance(surfaces, list) else []:
+            if (
+                isinstance(surface, dict)
+                and isinstance(surface.get("id"), str)
+                and (owner, surface["id"]) in ambiguous_surface_ids
+                and surface.get("disposition") == "needs_follow_up"
+                and not any(key in surface for key in ("candidateId", "candidate", "finding"))
+                and isinstance(coverage.get("surfaces"), list)
+            ):
+                retained_surface = {**surface, "receiptRefs": surface.get("receiptRefs", [])}
+                if retained_surface not in coverage["surfaces"]:
+                    coverage["surfaces"].append(copy.deepcopy(retained_surface))
+    selected_terminal_orders: dict[str, tuple[int, int]] = {}
+    for relative, draft, worker_id in sources:
+        if (
+            worker_id is not None
+            and relative in selected_observations
+            and draft.get("complete") is not False
+        ):
+            order = source_order[relative]
+            selected_terminal_orders[worker_id] = max(
+                selected_terminal_orders.get(worker_id, order), order
+            )
+    terminal_worker_orders: dict[str | None, tuple[int, int]] = {}
+    for relative, draft, worker_id in sources:
+        if relative in current_results and draft.get("complete") is not False:
+            order = source_order[relative]
+            terminal_worker_orders[worker_id] = max(
+                terminal_worker_orders.get(worker_id, order), order
+            )
     for relative, draft, worker_id in all_sources:
+        worker_result_order = terminal_worker_orders.get(worker_id)
+        selected_coverage_superseded = worker_id in selected_terminal_orders and (
+            source_order[relative] < selected_terminal_orders[worker_id]
+            or (
+                relative in current_results
+                and source_order[relative] <= selected_terminal_orders[worker_id]
+            )
+        )
+        selected_candidates = {
+            candidate_id
+            for (owner, candidate_id), (_, source) in ordered_outcomes.items()
+            if owner == worker_id and source == relative
+        }
         superseded = (
             worker_id is None
             and parent is not None
             and parent.get("complete") is not False
             and relative != "parent"
+            and source_order[relative] <= (0, parent_modified)
             and (not stopped_parent_seal or relative in parent_preserved_sources)
         ) or (
             relative not in current_results
-            and any(
-                saved_worker == worker_id
-                and saved_path in current_results
-                and current.get("complete") is not False
-                for saved_path, current, saved_worker in sources
+            and worker_result_order is not None
+            and (
+                relative not in selected_observations
+                or source_order[relative] < worker_result_order
             )
         )
+        # A failed result write can leave pending work outside the accepted result.
+        accepted_order = (
+            (0, parent_modified)
+            if worker_id is None and parent is not None
+            else worker_result_order
+        )
+        retain_pending = (
+            superseded
+            and (worker_id in headed_workers or (worker_id is None and parent_heads))
+            and accepted_order is not None
+            and source_order[relative] >= accepted_order
+            and any(isinstance(row, dict) for row in deferred_rows[relative])
+        )
         if (
-            (relative != "parent" or not parent_manifest)
+            (relative != "parent" or not parent_is_canonical)
             and not superseded
+            and not selected_coverage_superseded
             and (
                 draft.get("complete") is False
                 or draft["coverage"].get("completeness") != "complete"
@@ -814,16 +1713,56 @@
             and coverage.get("completeness") in {"complete", "unknown"}
         ):
             coverage["completeness"] = "partial"
-        if (
+        skip_superseded_findings = (
             superseded
             and not stopped
             and all(valid_finding(finding) for finding in (parent["findings"] if parent else []))
-        ):
+        )
+        if skip_superseded_findings and not selected_candidates and not retain_pending:
             continue
-        if "threatModel" not in manifest["scan"] and isinstance(draft.get("threatModel"), dict):
-            manifest["scan"]["threatModel"] = copy.deepcopy(draft["threatModel"])
+        if (
+            not skip_superseded_findings
+            and "threatModel" not in manifest["scan"]
+            and isinstance(draft.get("threatModel"), dict)
+        ):
+            model = draft["threatModel"]
+            model_path = relative
+            checkpoint_dir = Path(relative).parent
+            prefer_worker_head = worker_id is not None and (
+                checkpoint_dir.name == "checkpoints" or draft.get("complete") is False
+            )
+            if worker_id is not None:
+                if checkpoint_dir.name != "checkpoints":
+                    checkpoint_dir /= "checkpoints"
+                selected_models = [
+                    path
+                    for path in selected_observations
+                    if Path(path).parent == checkpoint_dir
+                    and isinstance(drafts_by_path[path].get("threatModel"), dict)
+                ]
+                if selected_models:
+                    head_path = max(selected_models, key=source_order.__getitem__)
+                    current = drafts_by_path[head_path]
+                    # A terminal checkpoint is committed before result.json is replaced.
+                    # Use the admitted observation, including its frozen ordering on retries.
+                    if not prefer_worker_head and current.get("complete") is not False:
+                        prefer_worker_head = source_order[head_path] >= source_order[relative]
+                    if prefer_worker_head:
+                        model = current["threatModel"]
+                        model_path = head_path
+            manifest["scan"]["threatModel"] = copy.deepcopy(model)
+            if worker_id is not None:
+                manifest["scan"]["threatModel"]["origin"] = "recovered"
+            if selected_model_source is not None and worker_id is not None:
+                selected_model_source[:] = [model_path]
         for value in draft["findings"]:
-            if relative == "parent" and parent_manifest:
+            if skip_superseded_findings and not (
+                isinstance(value, dict)
+                and (candidate_id := finding_candidate_id(value)) in selected_candidates
+                and resolved.get((worker_id, candidate_id)) == "reported"
+            ):
+                continue
+            if relative == "parent" and parent_is_canonical:
                 finding = copy.deepcopy(value)
                 _ensure_finding_identity(finding, candidate_only=True)
                 provenance = finding.get("provenance") if isinstance(finding, dict) else None
@@ -846,7 +1785,6 @@
             if not isinstance(value, dict):
                 warnings.append(f"Retained malformed finding evidence in {relative}.")
                 continue
-            source_value = copy.deepcopy(value)
             finding = copy.deepcopy(value)
             candidate_id = finding_candidate_id(finding)
             if relative != "parent" and resolved.get((worker_id, candidate_id)) in {
@@ -909,24 +1847,19 @@
                     historical_contents = set()
                 if mapped_key is not None:
                     key = mapped_key
-                    represented_by_parent = (
-                        _digest(_finding_content(source_value)) in historical_contents
-                    )
+                    represented_by_parent = _digest(_finding_content(value)) in historical_contents
             if key in finding_positions:
                 retained = findings[finding_positions[key]]
                 if finding != retained:
-                    if represented_by_parent:
-                        previous = copy.deepcopy(source_value)
-                        previous_history = previous.get("provenance", {}).pop(
-                            "previousFindings", []
-                        )
-                    elif _finding_strength(finding) > _finding_strength(retained):
+                    if not represented_by_parent and _finding_strength(finding) > _finding_strength(
+                        retained
+                    ):
                         previous = copy.deepcopy(retained)
                         previous_history = previous["provenance"].pop("previousFindings", [])
                         retained = finding
                         findings[finding_positions[key]] = retained
                     else:
-                        previous = copy.deepcopy(source_value)
+                        previous = copy.deepcopy(value)
                         previous_history = previous.get("provenance", {}).pop(
                             "previousFindings", []
                         )
@@ -960,10 +1893,14 @@
                 continue
             finding_positions[key] = len(findings)
             findings.append(finding)
-        if superseded:
+        if superseded and not selected_candidates and not retain_pending:
             continue
         for field in ("surfaces", "explicitExclusions", "deferred", "openQuestions"):
+            if superseded and field not in {"surfaces", "explicitExclusions", "deferred"}:
+                continue
             items = draft["coverage"].get(field, [])
+            if field == "deferred":
+                items = deferred_rows[relative]
             if not isinstance(items, list):
                 continue
             output = coverage.setdefault(field, [])
@@ -972,6 +1909,40 @@
                 # recovery and warnings rather than silently changing its contract.
                 continue
             for item in items:
+                # A selected terminal checkpoint replaces ordinary coverage, while
+                # candidate evidence and pending tasks retain their own reconciliation.
+                if (
+                    selected_coverage_superseded
+                    and field != "deferred"
+                    and not (
+                        isinstance(item, dict)
+                        and any(key in item for key in ("candidateId", "candidate", "finding"))
+                    )
+                ):
+                    continue
+                # A selected outcome must retain its evidence even if its result write failed.
+                if superseded and not (
+                    isinstance(item, dict)
+                    and (
+                        (field == "deferred" and retain_pending)
+                        or (
+                            isinstance(item.get("candidateId"), str)
+                            and item["candidateId"] in selected_candidates
+                            and item.get("disposition")
+                            == resolved.get((worker_id, item["candidateId"]))
+                        )
+                    )
+                ):
+                    continue
+                if id(item) in replaced_rows.get(field, ()):
+                    continue
+                if (
+                    field == "deferred"
+                    and isinstance(item, dict)
+                    and isinstance(item.get("id"), str)
+                    and (worker_id, item["id"]) in closed_deferred
+                ):
+                    continue
                 if field == "openQuestions" and isinstance(item, str):
                     item = {"question": item.strip()}
                 if (
@@ -995,14 +1966,20 @@
                             history.append(copy.deepcopy(finding))
                 if (
                     isinstance(item, dict)
-                    and (worker_id, item.get("candidateId")) in resolved
+                    and isinstance(
+                        identity := _deferred_candidate_id(item, worker_id, ambiguous_deferred)
+                        if field == "deferred"
+                        else item.get("candidateId"),
+                        str,
+                    )
+                    and (worker_id, identity) in resolved
                     and (field == "deferred" or item.get("disposition") == "needs_follow_up")
                 ):
                     continue
+                if field == "surfaces" and isinstance(item, dict):
+                    item = {**item, "receiptRefs": item.get("receiptRefs", [])}
                 if isinstance(item, dict) and "id" not in item:
                     semantic_item = dict(item)
-                    if field == "surfaces":
-                        semantic_item.setdefault("receiptRefs", [])
                     if any(
                         isinstance(existing, dict)
                         and {key: value for key, value in existing.items() if key != "id"}
@@ -1036,7 +2013,10 @@
                 if isinstance(item.get("id"), str):
                     used.add(item["id"])
                 continue
-            item.setdefault("id", item.get("candidateId") or f"saved-{_digest(item)[:16]}")
+            item.setdefault("id", _saved_coverage_id(item))
+            if not isinstance(item["id"], str):
+                # Preserve malformed rows for per-record recovery, including frozen replay.
+                continue
             if item["id"] in used:
                 item["id"] = f"{item['id']}-{_digest(item)[:16]}"
             used.add(item["id"])
@@ -1081,6 +2061,9 @@
         except ContractError:
             path = scan_dir / relative
             if path.exists() or path.is_symlink():
+                if relative == "threatmodel.md":
+                    # This optional projection will not replace an unsafe destination.
+                    continue
                 raise
             snapshots[relative] = None
         finally:
@@ -1112,13 +2095,17 @@
     if scan["status"] != "failed":
         return False
     frozen_source_digests: dict[str, str] | None = None
+    model_source: list[str] = []
+    saved_model_source: str | None = None
     raw_frozen_sources = scan["retained_source_digests_json"]
+    if raw_frozen_sources is not None:
+        frozen_source_digests, saved_model_source = _retained_source_state(
+            json.loads(raw_frozen_sources)
+        )
+        if saved_model_source is not None:
+            model_source.append(saved_model_source)
     if recovery_source_digests is not None:
         frozen_source_digests = recovery_source_digests
-    elif raw_frozen_sources is not None:
-        frozen_source_digests = _source_digests(
-            json.loads(raw_frozen_sources), "Saved stopped-scan"
-        )
     scan_dir = db.require_canonical_scan_directory(Path(scan["scan_dir"]))
     deep_run = connection.execute(
         "SELECT status FROM deep_scan_runs WHERE scan_id = ?", (scan_id,)
@@ -1141,12 +2128,10 @@
     ]
 
     def record_publication(manifest: dict[str, Any], findings: dict[str, Any]) -> None:
-        retained_sources = manifest.get("scan", {}).get("preservedSources")
-        if not isinstance(retained_sources, dict) or not all(
-            isinstance(relative, str) and isinstance(source_digest, str)
-            for relative, source_digest in retained_sources.items()
-        ):
-            raise ContractError("Stopped scan source digests could not be frozen.")
+        retained_sources = _source_digests(
+            manifest.get("scan", {}).get("preservedSources"),
+            "Stopped scan source digests could not be frozen.",
+        )
         digest = db.published_manifest_digest(scan_dir, manifest)
         timestamp = db.now()
         with connection:
@@ -1176,7 +2161,7 @@
                 "updated_at = ? WHERE id = ? AND status = 'failed'",
                 (
                     digest,
-                    json.dumps(retained_sources, sort_keys=True),
+                    _encode_retained_sources(retained_sources, model_source),
                     json.dumps(list(dict.fromkeys(warnings))),
                     timestamp,
                     scan_id,
@@ -1190,6 +2175,7 @@
 
     existing_path = db.artifact_path(scan_dir, db.ARTIFACTS["manifest"], required=False)
     existing_scan = db.read_json_object(existing_path).get("scan", {}) if existing_path else {}
+    existing = None
     if scan["seal_manifest_digest"] is not None or (
         isinstance(existing_scan, dict)
         and (
@@ -1198,30 +2184,35 @@
     ):
         db.require_recorded_manifest_digest(scan, scan_dir)
         existing, existing_findings, _ = finalize_scan(
-            scan_dir, expected_coverage_mode=db.expected_coverage_mode(scan)
+            scan_dir,
+            expected_coverage_mode=db.expected_coverage_mode(scan),
+            projection_warnings=warnings,
         )
         db.verify_manifest_binding(scan, existing)
         if existing_scan.get("status") == outcome:
             existing_sources = existing_scan.get("preservedSources")
             if frozen_source_digests is None:
-                if not isinstance(existing_sources, dict) or not all(
-                    isinstance(relative, str) and isinstance(digest, str)
-                    for relative, digest in existing_sources.items()
-                ):
-                    raise ContractError("Stopped scan source digests could not be frozen.")
-                frozen_source_digests = existing_sources
+                frozen_source_digests = _source_digests(
+                    existing_sources, "Stopped scan source digests could not be frozen."
+                )
             if existing_sources == frozen_source_digests:
                 if (
                     raw_frozen_sources is not None
                     and scan["seal_manifest_digest"] is not None
                     and not publication_follow_up_warnings
+                    and warnings == stored_warnings
                 ):
                     return True
                 record_publication(existing, existing_findings)
                 return True
             if recovery_source_digests is None:
                 raise ContractError("Stopped scan sources changed after terminal publication.")
-    binding = {**db.workbench_completion_binding(scan, scan["completed_at"]), "status": outcome}
+    binding = {
+        **db.workbench_completion_binding(scan, scan["completed_at"], existing),
+        "status": outcome,
+    }
+    if recovery_source_digests is not None:
+        model_source.clear()
     documents = merge_saved_results(
         scan_dir,
         scan_id,
@@ -1237,6 +2228,8 @@
             f"{scan['failure_message'] or ''}"
         ).strip(),
         frozen_source_digests=frozen_source_digests,
+        frozen_model_source=model_source[0] if model_source else None,
+        selected_model_source=model_source,
         allow_frozen_legacy_parent=(
             include_parent_with_recovery
             or (
@@ -1258,18 +2251,22 @@
                     (json.dumps(unpublished_warnings), db.now(), scan_id),
                 )
         return False
-    if frozen_source_digests is None:
-        retained_sources = documents[0].get("scan", {}).get("preservedSources")
-        if not isinstance(retained_sources, dict) or not all(
-            isinstance(relative, str) and isinstance(digest, str)
-            for relative, digest in retained_sources.items()
-        ):
-            raise ContractError("Stopped scan source digests could not be frozen.")
+    if frozen_source_digests is None or (
+        recovery_source_digests is None and saved_model_source is None and model_source
+    ):
+        retained_sources = _source_digests(
+            documents[0].get("scan", {}).get("preservedSources"),
+            "Stopped scan source digests could not be frozen.",
+        )
         with connection:
             connection.execute(
                 "UPDATE scans SET retained_source_digests_json = ? "
-                "WHERE id = ? AND retained_source_digests_json IS NULL",
-                (json.dumps(retained_sources, sort_keys=True), scan_id),
+                "WHERE id = ? AND retained_source_digests_json IS ?",
+                (
+                    _encode_retained_sources(retained_sources, model_source),
+                    scan_id,
+                    raw_frozen_sources,
+                ),
             )
     prepared = _prepare_scan_finalization(
         scan_dir,
@@ -1280,7 +2277,9 @@
     )
     snapshots = _snapshot_published_outputs(scan_dir)
     try:
-        manifest, findings, _ = _write_prepared_scan_finalization(prepared)
+        manifest, findings, _ = _write_prepared_scan_finalization(
+            prepared, projection_warnings=warnings
+        )
         db.verify_manifest_binding(scan, manifest)
         record_publication(manifest, findings)
     except BaseException:
@@ -1399,6 +2398,28 @@
                 "The scan stopped; its saved checkpoint was retained without replacing sealed results."
             )
         scan_dir = db.require_canonical_scan_directory(Path(scan["scan_dir"]))
+        if (
+            args.expected_draft_digest is not None
+            and args.expected_draft_digest != _scan_draft_digest(scan_dir)
+        ):
+            raise SystemExit(
+                "scan_draft_conflict: canonical scan results changed; reconcile the saved checkpoint again."
+            )
+        try:
+            relative = Path(args.draft_path).relative_to(scan_dir).as_posix()
+        except ValueError as exc:
+            raise SystemExit(
+                "Scan draft must be inside the registered scan drafts directory."
+            ) from exc
+        if not re.fullmatch(r"drafts/[0-9a-fA-F-]+\.json", relative):
+            raise SystemExit("Scan draft must be inside the registered scan drafts directory.")
+        draft = _read_scan_local_json(scan_dir, relative, "Staged scan draft")
+        manifest, findings, coverage = draft["manifest"], draft["findings"], draft["coverage"]
+        binding = db.workbench_completion_binding(scan, db.now())
+        # Save scan IDs without sealing the draft.
+        _populate_unsealed_manifest_envelope(manifest, manifest["scan"], binding)
+        _populate_unsealed_artifact_envelope(manifest, findings, coverage, binding)
+        _validate_completion_binding(manifest, findings, coverage, binding)
         if args.checkpoint_path is not None:
             try:
                 checkpoint_relative = Path(args.checkpoint_path).relative_to(scan_dir).as_posix()
@@ -1421,33 +2442,15 @@
                 f"checkpoints/{checkpoint_digest}.json",
                 checkpoint_contents,
             )
-        if (
-            args.expected_draft_digest is not None
-            and args.expected_draft_digest != _scan_draft_digest(scan_dir)
-        ):
-            raise SystemExit(
-                "scan_draft_conflict: canonical scan results changed; reconcile the saved checkpoint again."
-            )
-        try:
-            relative = Path(args.draft_path).relative_to(scan_dir).as_posix()
-        except ValueError as exc:
-            raise SystemExit(
-                "Scan draft must be inside the registered scan drafts directory."
-            ) from exc
-        if not re.fullmatch(r"drafts/[0-9a-fA-F-]+\.json", relative):
-            raise SystemExit("Scan draft must be inside the registered scan drafts directory.")
-        draft = _read_scan_local_json(scan_dir, relative, "Staged scan draft")
-        manifest, findings, coverage = draft["manifest"], draft["findings"], draft["coverage"]
-        binding = db.workbench_completion_binding(scan, db.now())
-        # Validate on copies: saved canonical documents remain ordinary unsealed drafts.
-        copied_manifest = copy.deepcopy(manifest)
-        copied_findings = copy.deepcopy(findings)
-        copied_coverage = copy.deepcopy(coverage)
-        _populate_unsealed_manifest_envelope(copied_manifest, copied_manifest["scan"], binding)
-        _populate_unsealed_artifact_envelope(
-            copied_manifest, copied_findings, copied_coverage, binding
+        checkpoint = _parent_scan_draft(scan_id, manifest["scan"], findings, coverage)
+        checkpoint_contents = _encoded(checkpoint)
+        checkpoint_name = f"{hashlib.sha256(checkpoint_contents).hexdigest()}.json"
+        checkpoint_relative = f"checkpoints/{checkpoint_name}"
+        if not (scan_dir / checkpoint_relative).exists():
+            write_scan_local_bytes(scan_dir, checkpoint_relative, checkpoint_contents)
+        write_scan_local_bytes(
+            scan_dir, "checkpoint-head.json", _encoded({"checkpoint": checkpoint_name})
         )
-        _validate_completion_binding(copied_manifest, copied_findings, copied_coverage, binding)
         for filename, document in (
             ("findings.json", findings),
             ("coverage.json", coverage),
@@ -1458,9 +2461,17 @@
                 filename,
                 (json.dumps(document, allow_nan=False, indent=2) + "\n").encode(),
             )
+        model_warning = write_threat_model_projection_if_possible(scan_dir, manifest)
         # Accepted Standard drafts are evidence of review or report assembly,
         # even when the parent omitted its explicit progress call.
-        if scan["mode"] == "standard":
+        model_only_checkpoint = (
+            manifest["scan"].get("complete") is False
+            and isinstance(manifest["scan"].get("threatModel"), dict)
+            and not findings.get("findings")
+            and not coverage.get("surfaces")
+            and not coverage.get("deferred")
+        )
+        if scan["mode"] == "standard" and not model_only_checkpoint:
             phase = "discovery" if manifest["scan"].get("complete") is False else "reporting"
             earlier = PHASES[: PHASES.index(phase)]
             placeholders = ",".join("?" for _ in earlier)
@@ -1481,7 +2492,11 @@
                         )
             except sqlite3.Error as exc:
                 print(f"Could not save scan progress: {exc}", file=sys.stderr)
-    return {"scanId": scan_id, "status": "draft_written"}
+    return {
+        "scanId": scan_id,
+        "status": "draft_written",
+        **({"warnings": [model_warning]} if model_warning else {}),
+    }
 
 
 def _scan_draft_digest(scan_dir: Path) -> str:
@@ -1494,9 +2509,10 @@
         except FileNotFoundError:
             digest.update(b"missing\0")
             continue
-        _, contents = _read_scan_local_json_bytes(scan_dir, filename, filename)
         digest.update(b"present\0")
-        digest.update(contents)
+        descriptor = open_scan_local_file_descriptor(scan_dir, filename, filename)
+        with os.fdopen(descriptor, "rb") as handle:
+            digest.update(handle.read())
         digest.update(b"\0")
     return digest.hexdigest()
 
Full technical diff · 148 changed fields

changed /files/.app.json/sha256

BEFORE
"e4d5b22326ee380de5d779f7b5ba590c8d1bee9a80e8869fb2bd7f4def8e974d"
AFTER
"a7bbe314059c7311e169474823e3978f267e560c3e961ba49fb94032da9843fe"

changed /files/.app.json/size

BEFORE
480
AFTER
479

changed /files/.codex-plugin~1plugin.json/sha256

BEFORE
"3ddcc187fe2f1aff961bf55444e70bfbc3f825b6185ab3f29860d1a813a395f9"
AFTER
"527d1a0db286884729572d400b0ad9b0ff852d4a12b36228ff5fbef180f7de9d"

changed /files/.mcp.json/sha256

BEFORE
"40b84baf510169eadfd5f0fe0ad95001caedb7bc662d77c9ac2a4fbec7dd0845"
AFTER
"7c5968060f6e848859ce02450d6e02e67029d8d39011bbda1059b19de6309ebf"

changed /files/.mcp.json/size

BEFORE
1649
AFTER
1679

changed /files/examples~1completed-scan~1report.md/sha256

BEFORE
"c2083b2f763ed5ddcee4524ee01025c9b8996e1e9740263d751494b7a61fe82e"
AFTER
"83b7c709c0b49d1500b52e61a88ea70b8423ebebec38308b1294cf094838b66f"

changed /files/examples~1completed-scan~1report.md/size

BEFORE
2945
AFTER
4116

changed /files/integrity.json/sha256

BEFORE
"d8f7683d4ed54915958087c1532c15a0eb0e87db1424addea133794979b609cc"
AFTER
"6b342df66fb9b5ce7fc8c9de0e974b59d21340cdc06167df46d1a19bd4ea9441"

changed /files/integrity.json/size

BEFORE
17616
AFTER
17465

changed /files/mcp~1helpers.mjs.br.part-000/sha256

BEFORE
"a771236cf650b155f4afc89169897ca199bafaa673187b781904df8bee8af96a"
AFTER
"6299f73c92c4f924bffc6b1bed001480603473967006b3e75941bc49a21f18d5"

changed /files/mcp~1helpers.mjs.br.part-000/size

BEFORE
6539
AFTER
63398

changed /files/mcp~1native~1THIRD_PARTY_NOTICES.txt/sha256

BEFORE
"bcb3277e216ef458f18b80ddf97e1b7c2827e920a2c432077ff6f7d060281445"
AFTER
"16ac63908855f5bb7eab864923cc1855b406bd7051dfcd70d7e69684f6c69955"

changed /files/mcp~1native~1darwin-arm64~1unix.node/sha256

BEFORE
"1895d5afd007ce02210075683413be6088ea20af8a8608a42ac82b7d9bf90181"
AFTER
"093e87d8de3d2676fc9a2a1430f571bf4fc970b2bbef4c7ded0bf8924e45f473"

changed /files/mcp~1native~1darwin-arm64~1unix.node/size

BEFORE
371616
AFTER
354352

changed /files/mcp~1native~1darwin-x64~1unix.node/sha256

BEFORE
"94aba646f50e669b873b44f78512886dcf78be87a13feb95471e687fdc1f9f91"
AFTER
"57c45d212d35ce7599afc7b9b21693e18443753231bad1fb5146a4e224db68ec"

changed /files/mcp~1native~1darwin-x64~1unix.node/size

BEFORE
364544
AFTER
343296

changed /files/mcp~1native~1linux-arm64-gnu~1unix.node/sha256

BEFORE
"bd89532fbd21d251d9c645d6026864b0a08b16565bedb018e627e459cae0ddef"
AFTER
"6b352aaaac21f9ad9b772481429ba7bc8433a0e44ca5ffe433ad47289c3007f6"

changed /files/mcp~1native~1linux-arm64-gnu~1unix.node/size

BEFORE
465648
AFTER
465088

changed /files/mcp~1native~1linux-arm64-musl~1unix.node/sha256

BEFORE
"65475c3deb4ed355739ad7395e39204f5ffcc945c61b2e42ca357560de3fe7de"
AFTER
"3a55ec4f42202c58a29654701c31a8deab386969d14e2ab85f23e81345b4fedb"

changed /files/mcp~1native~1linux-arm64-musl~1unix.node/size

BEFORE
397992
AFTER
332456

changed /files/mcp~1native~1linux-x64-gnu~1unix.node/sha256

BEFORE
"d19c3d41b2993543a1296dc38d2183df4a947349b45f3c43618c4ecde74e3cac"
AFTER
"ed2061078400010852a9f8d3ecdf7bc0a008d13adc9e9d5c6954f5cb686a2a2a"

changed /files/mcp~1native~1linux-x64-gnu~1unix.node/size

BEFORE
386424
AFTER
365352

changed /files/mcp~1native~1linux-x64-musl~1unix.node/sha256

BEFORE
"46fa397234fad923a2e325f80e2a833ba48c79b32bbb7f0452fb7f0791de5d5e"
AFTER
"c2e4c07ac6dde175464c6315937652465b200e5f28590898663523beaf4e9ab6"

changed /files/mcp~1native~1linux-x64-musl~1unix.node/size

BEFORE
385688
AFTER
369304

changed /files/mcp~1native~1win32-arm64~1windows.node/sha256

BEFORE
"df8dd512fd8d92a8af3fede3f00a235732f847c6f3da91a2b9a702650ddd7439"
AFTER
"19f78e88fd8934038201c26b044123bc05db74f5d7065df10a32583ccbcf7a55"

changed /files/mcp~1native~1win32-arm64~1windows.node/size

BEFORE
460800
AFTER
419840

changed /files/mcp~1native~1win32-x64~1windows.node/sha256

BEFORE
"f961856d20361508fa3b5ad05a4fb04f07d0dff4d7b60f3cfc377467b518c17b"
AFTER
"5aed21edb5fe06efac49d9bd3899d6242f60b1bfac2b710b967a76c924d98298"

changed /files/mcp~1native~1win32-x64~1windows.node/size

BEFORE
496128
AFTER
453120

changed /files/mcp~1server.mjs.br.part-000/sha256

BEFORE
"0259e86ae3ceb3c555521d959fd9cddb0d67e2921fc1db9fd27a55d849a48806"
AFTER
"7ef9c7387b405b51e2aec793a86696bf793506a8f34155199035aa515b3a415a"

changed /files/mcp~1server.mjs.br.part-001/sha256

BEFORE
"3c6c93cd01ac739436e91ffe2685c5267672cf18794dbe1b19ac1ac5f93d421f"
AFTER
"94d6412009e284ecbc17bd14d07e0116d8e74e572a62198912ac5a03f95a50c2"

changed /files/mcp~1server.mjs.br.part-001/size

BEFORE
122535
AFTER
126028

changed /files/references~1artifact-storage.md/sha256

BEFORE
"851076fcdc93c7adb87f3e26ff103c5546b875e2bc08ddb676fbbca0827899b1"
AFTER
"ccce9c76f5d044fbce333d6129fbb55eb63f512c15c2ebeeca405e296f38325b"

changed /files/references~1artifact-storage.md/size

BEFORE
6975
AFTER
7632

changed /files/references~1config-preflight.md/sha256

BEFORE
"6ba05681c8d94968842fc98553b58b8fc5f1ea3da45834a41dd60179c8bbb9f3"
AFTER
"5894f9fd7efcae7034404a2c062ee3d7417520753be16ddcdb3acf5b884f048d"

changed /files/references~1config-preflight.md/size

BEFORE
18881
AFTER
19162

changed /files/references~1core-scan.md/sha256

BEFORE
"77b082eb8613cf93427ff730e4ae5d85b0a0dca37c02a8af1ea69f679ac3d1d9"
AFTER
"9c0eca53147383a89cd345eac7791ea75b954f7f083448ef25019d8f9ba30afe"

changed /files/references~1core-scan.md/size

BEFORE
19422
AFTER
22978

changed /files/references~1final-report.md/sha256

BEFORE
"9fe5a42b37964def3a8394449c3502e8b6ee50b8f85e6525313e23ba2dcd5040"
AFTER
"9c34375584c8ae76959b0eae038b86b2b8ed7874517c249fcae8715314c76298"

changed /files/references~1final-report.md/size

BEFORE
29052
AFTER
29931

changed /files/references~1scan-artifacts.md/sha256

BEFORE
"d8d416b359b9a46bd3d9cf9fb487d1ff2dc6eb32dc26e93ab47de7b1d240f479"
AFTER
"eecdccac30b8ad65bb702dc9ac8d9718dcb0b95532a94a61195268d22bdf504b"

changed /files/references~1scan-artifacts.md/size

BEFORE
10800
AFTER
11243

changed /files/references~1scan-contract.md/sha256

BEFORE
"8a84ca15feef973044bfe619b4e586bf9323b9cbc2f386b961138c76ebd6d189"
AFTER
"b362636c29d1e8b18053481abe2f97d7bb099a97040f5b63aa2702a41c1d62c8"

changed /files/references~1scan-contract.md/size

BEFORE
14043
AFTER
14537

changed /files/references~1threat-model.md/sha256

BEFORE
"f53d10c617d45d671e7cb15408f87c4f36b29026e2a6a59d1457881780b6818a"
AFTER
"f3dd2d05d7f0c61b13d29a78e810b367772fe22d5ac58adbbc00080a9495706e"

changed /files/references~1threat-model.md/size

BEFORE
14690
AFTER
15203

changed /files/schemas~1coverage.schema.json/sha256

BEFORE
"7964b132998ca4dcdd19c75f5d92483e1d44cb71462237709b968ec548c10652"
AFTER
"6ac14e659e884e9582d8bd9ba480f6364ca17309e1a3dee9469ab025a3380b11"

changed /files/schemas~1coverage.schema.json/size

BEFORE
4670
AFTER
5198

changed /files/schemas~1scan-manifest.schema.json/sha256

BEFORE
"265a48629113f77cd65a3127f1f7e95d3c39ae60e868685837a6aa31d4133310"
AFTER
"c621d4136ac81741ac8da1960c7f5e92ade22c0b5490e378ea4339b09e45b6f3"

changed /files/schemas~1scan-manifest.schema.json/size

BEFORE
8048
AFTER
9513

changed /files/schemas~1tools~1scan-draft.schema.json/sha256

BEFORE
"d5970c06e2ec00d1da5b0e84b677244f32046fd99c81901e608b7974b9ab0d89"
AFTER
"151f9dffc83e1c5b4f0a7fba20b90fb3196fdaa9719180eb0009ba2b50f75d48"

changed /files/schemas~1tools~1scan-draft.schema.json/size

BEFORE
21535
AFTER
23475

changed /files/scripts~1config_preflight.py/sha256

BEFORE
"37b9f4cee4c4dddade0e8599ea8df19aee29a99bd1be956b553ab46c3b63d407"
AFTER
"f4d4103650977d078b71f49da6b1e287b6db2f2b7b5fb08e943722a6d869354e"

changed /files/scripts~1config_preflight.py/size

BEFORE
35211
AFTER
35176

changed /files/scripts~1deep_scan_workbench.py/sha256

BEFORE
"c659739775e192dee0f90a42549a1763fb95228fb5fd17579812ab454bfc7d21"
AFTER
"5012b337503f44e086fbf021be9db8340f8ba992fe5169c5534752e900947cf5"

changed /files/scripts~1deep_scan_workbench.py/size

BEFORE
88194
AFTER
84709

changed /files/scripts~1filesystem_identity.py/sha256

BEFORE
"2b3a22761f28faa2f45f192ff14b17fd80f1f43d9d4174007c51d21f4cbf47c1"
AFTER
"5298f2012b6e48ab1106837a5440ef2b3966383e2321507a47593010bb4dd01f"

changed /files/scripts~1filesystem_identity.py/size

BEFORE
926
AFTER
893

changed /files/scripts~1finalize_scan_contract.py/sha256

BEFORE
"7f191c50ded50600b0a70be1e11b724f0355f166a690312226990d88e895d159"
AFTER
"3fa92d8e2ccadb032cf458f0fde444785e8ee5ced0abe6346c285b07ef153e27"

changed /files/scripts~1finalize_scan_contract.py/size

BEFORE
121437
AFTER
133770

changed /files/scripts~1finding_preview.py/sha256

BEFORE
"0df185e83a55cfcd9da151054de32b04e8f0bc0ceee3a2d9269d0d119fc12854"
AFTER
"ebdf8e7b7e465c0937e73e945e3593d659b2f15ff1db08d0b4a2d3b106174c4d"

changed /files/scripts~1finding_preview.py/size

BEFORE
15968
AFTER
16089

changed /files/scripts~1generate_in_scope_files.py/sha256

BEFORE
"e59583c111b4f3edd3f57e114bfb7f62a7dbad8424b4851bdfd23a8219336806"
AFTER
"0b420d786b17e4f91451b5b686fdb4d6beee3913eebb952f8fcbf220aec6230e"

changed /files/scripts~1generate_in_scope_files.py/size

BEFORE
11801
AFTER
11646

changed /files/scripts~1generate_rank_input.py/sha256

BEFORE
"0c0ead74ae98b4adfbd6a2eacb91ea492b11e60434c4259ae36213c0ddc5f3f8"
AFTER
"e7c0bddbdb7e9236f7431d84b86be4c3fc202daa8d1306d6baa930110635b671"

changed /files/scripts~1generate_rank_input.py/size

BEFORE
47421
AFTER
17899

changed /files/scripts~1launch_codex_security_mcp/sha256

BEFORE
"b82af11478dc85f666b6ec0919c53034dd287c29ffcb1af7e5debbb425618c5d"
AFTER
"56794e736a1e8f588f9959a2e707ea3ed9bc7d4d2b796c132e91d5fbed59e600"

changed /files/scripts~1launch_codex_security_mcp/size

BEFORE
1228
AFTER
1793

removed /files/scripts~1normalize_candidates.py

BEFORE
{
  "sha256": "8da9b66deec6320a3e55c033a097480bedd340f0442b31077999a2e2d82e851e",
  "size": 13136
}
AFTER
Field is absent

changed /files/scripts~1rank_preview.py/sha256

BEFORE
"013885da19308feeb0bcea8968097ba8eee8b063b23a6ca5af8a7269ca777afa"
AFTER
"627d0b89c9d4436162ab9bcb80f7aa562605695e784dc612307c7eda6fef3a7f"

changed /files/scripts~1rank_preview.py/size

BEFORE
34987
AFTER
34726

changed /files/scripts~1report_projection.py/sha256

BEFORE
"474de8e927442b1c5c7fc4adb5addd98906ae86db05da1aa20bd54ad5fca48fb"
AFTER
"1b24978a3cdb3c336ac27a2b39fb35457bade13a4aad0782710fe38ac39965cf"

changed /files/scripts~1report_projection.py/size

BEFORE
41587
AFTER
41098

changed /files/scripts~1snapshot_sqlite.py/sha256

BEFORE
"a501b0f4b6656f4f9b6a389835e73f0d8105051191646b64af4268f0d4a3eaee"
AFTER
"9e0be851702f0962070ece6e0e2959e3955fc99126f68452d175fc22bc38423b"

changed /files/scripts~1snapshot_sqlite.py/size

BEFORE
852
AFTER
1084

added /files/scripts~1threat_model_projection.py

BEFORE
Field was absent
AFTER
{
  "sha256": "32e4d5cd370a49cdc5658c3ded3d4d63ba7f41a44b34b67da87321048d088111",
  "size": 6711
}

changed /files/scripts~1windows_scan_local_files.py/sha256

BEFORE
"3034ec0b2e5d29fb8edbbd7237b91b1063b0245cdc546d85209129fef1ab2fbf"
AFTER
"c26276695435d134c1354048c3c74c3c6b0ed203158a93b63b67ef0b6bfab843"

changed /files/scripts~1windows_scan_local_files.py/size

BEFORE
24480
AFTER
24919

changed /files/scripts~1workbench~1storage.py/sha256

BEFORE
"973275d28c851b1b8408f7ac59e89a2a68057f2aecb4831e6aa2396c83a39e6d"
AFTER
"5e389073520fb7668defd433228befd9e7b9c8309c1b6024bffd6213d59f1d88"

changed /files/scripts~1workbench~1storage.py/size

BEFORE
608
AFTER
973

changed /files/scripts~1workbench_cli.py/sha256

BEFORE
"74752d032261afd831876d774724b150b35d6c8f0a3f70574c5c8ddad20f31b3"
AFTER
"959e951fa5daf326049ee61bd0773999be92600b65a9062e56b4f62fc4e2aa55"

changed /files/scripts~1workbench_cli.py/size

BEFORE
21184
AFTER
21260

changed /files/scripts~1workbench_constants.py/sha256

BEFORE
"06548d28c6d66b99f6c11f63918101c6cce39df6346fc6c5e977b9d09cd2a950"
AFTER
"0ff12b11b2ae9f71c1de6cff0afc234b035379bb1193f1ca9a56ddeb7aa32cf7"

changed /files/scripts~1workbench_constants.py/size

BEFORE
2261
AFTER
2187

changed /files/scripts~1workbench_db.py/sha256

BEFORE
"01d7d27013f24cf26cbca4a6e2331d7eb1bd0afc5e570c44376cd3e2e4fd5cb7"
AFTER
"94bf11d7d74202ae7efbdd4f70a036393d4ba013ffc2ada44172a59be41f97f2"

changed /files/scripts~1workbench_db.py/size

BEFORE
149800
AFTER
146186

changed /files/scripts~1workbench_finding_index.py/sha256

BEFORE
"410f4c2fb2bd063130e945d06aad63582b6a3e6fed9c448dcf740d85a6c3ce78"
AFTER
"4618f994786fa2300e78592fa2868bc0a585368127027c1940a0e504b0254265"

changed /files/scripts~1workbench_finding_index.py/size

BEFORE
4191
AFTER
3938

changed /files/scripts~1workbench_native_indexes.py/sha256

BEFORE
"0ada4190b7050fde77c3f04a8f36b1f6479da368aa1ed39c9587ce23645ba468"
AFTER
"114dbf1e4050302948c4b8f24d69454f127d128e3bc35e34a5f3ec9dd3b187cd"

changed /files/scripts~1workbench_native_indexes.py/size

BEFORE
9984
AFTER
9870

changed /files/scripts~1workbench_progress.py/sha256

BEFORE
"d106181710b59c3cd2d66a26d4a4556741d02d606999224a36cfb09771092a6e"
AFTER
"42c7d0b8b4d253095e1062922cd9e59b10b966ac314bef092cb78cad2217b967"

changed /files/scripts~1workbench_progress.py/size

BEFORE
13897
AFTER
13296

changed /files/scripts~1workbench_publication.py/sha256

BEFORE
"7813a522da6924e065a5a517efc54e80529c686773d084297f760e405b1cecd5"
AFTER
"9312a2962b797ed355d6b05fb375da6325aeb5c5ead5d52f2fb7114ef3ad9635"

changed /files/scripts~1workbench_publication.py/size

BEFORE
20574
AFTER
20346

changed /files/scripts~1workbench_remediation.py/sha256

BEFORE
"d0c7a4b3a283aea128802b4c1a4295580bc2ebd83c6c7108eb1ce288aaa20017"
AFTER
"96585805c4956917bb473c3b60493a3c15daa2028b074b756e36358a9ac76525"

changed /files/scripts~1workbench_remediation.py/size

BEFORE
7815
AFTER
7281

changed /files/scripts~1workbench_saved_results.py/sha256

BEFORE
"db445b651bd677359ef0a5c2bbb70e896eccd60becbd80e8c7a77aa3010f289c"
AFTER
"436b23a4d169006437985bb352bfd792da9e465c4c7506dce31bfd5e78524c6d"

changed /files/scripts~1workbench_saved_results.py/size

BEFORE
72501
AFTER
119202

changed /files/scripts~1workbench_scan_start.py/sha256

BEFORE
"8ce5f6e6d34670026f50da234c881074bd4864f41c9c574f6b2232ef968c7b1e"
AFTER
"647e54ce40a7dc5b17d6abe547406f22ca0cb4eee628938b4c21629eb8e73c35"

changed /files/scripts~1workbench_scan_start.py/size

BEFORE
8210
AFTER
7963

changed /files/scripts~1workbench_scan_usage.py/sha256

BEFORE
"4fb61d682ba05839ad57d33a8a2c9a9ffaa51ea7721c2cc7d629d2d687e2607a"
AFTER
"21d845f8967c9206351e54f94fd2b4bbfe834434e35b642992d7a829b76cd94e"

changed /files/scripts~1workbench_scan_usage.py/size

BEFORE
22317
AFTER
22094

changed /files/scripts~1workbench_schema.py/sha256

BEFORE
"6a382c792b39c0a41118a2d4325b967f3cb0dca2a2e6e84411e4e72a675372e7"
AFTER
"25ef8550ded14785fa47b19f1fa358422d8883affc94641c84e68c172e800cc0"

changed /files/scripts~1workbench_schema.py/size

BEFORE
58474
AFTER
51783

changed /files/scripts~1workbench_severity.py/sha256

BEFORE
"6313c3302a40504e8b1ebaabcc081bdcbbb97c9b348a7d63c381ea04042e898d"
AFTER
"ec74f540595e8052e295846303f31988329e0dc68fcebdd92572673555b1c99d"

changed /files/scripts~1workbench_severity.py/size

BEFORE
4489
AFTER
4625

changed /files/scripts~1workbench_source_excerpt.py/sha256

BEFORE
"0cd15d26c3a1feadf1858d1be25f9703ad09307ab1ecfb866260b8c1991cf74e"
AFTER
"5681d4a5c547404f687c644f54432c10e0770b6ba4c1e6be321664579a252a24"

changed /files/scripts~1workbench_source_excerpt.py/size

BEFORE
2989
AFTER
3210

changed /files/scripts~1workbench_target.py/sha256

BEFORE
"bc21dc8cb392a5639d787fde956f6b4d2769bf9c3f1cb805f164557df787563c"
AFTER
"08ed149295b9347ac37f2b8523c822a35f165283422c7644dabecfa236b81b72"

changed /files/scripts~1workbench_target.py/size

BEFORE
28827
AFTER
32451

changed /files/scripts~1workbench_target_state.py/sha256

BEFORE
"f48897fb7937e7474126dc88f85bcc359a1a99fb845a77f5d896dbe8b506b45a"
AFTER
"9c133f67397ab4b90ce1275edc09a7e6d05900236209bc72e8f113b9f0cde287"

changed /files/scripts~1workbench_target_state.py/size

BEFORE
1953
AFTER
1920

changed /files/scripts~1workbench_validation.py/sha256

BEFORE
"3cc29fc7a342dc9b55976c37c641c1e7a2abf91625fb9abfb1165a59e251ad29"
AFTER
"11259b8ce1e135c5a86677964f29e6331e65e5fd80a1931c80f7fac507cccb13"

changed /files/scripts~1workbench_validation.py/size

BEFORE
6773
AFTER
6547

changed /files/skills~1assess-patch-risk~1SKILL.md/sha256

BEFORE
"b7337a909fe0ea7f3b203178df60161ea42236405c0bb8697d43ec5736be744b"
AFTER
"c3b62a8c1d6135945a055134f50329047bb908972fc7b320a592edbc3d38b419"

changed /files/skills~1assess-patch-risk~1SKILL.md/size

BEFORE
8679
AFTER
9511

removed /files/skills~1assess-patch-risk~1scripts~1validate_patch_risk_assessment.py

BEFORE
{
  "sha256": "b57a804b1e30c94ed01d5a3a36f8a31b15a6e3b9f751a41c5ade07ff35019d77",
  "size": 6645
}
AFTER
Field is absent

changed /files/skills~1attack-path-analysis~1SKILL.md/sha256

BEFORE
"9f9f45603c68a1e796d8151b3373842aa7e89f0e1dc8e985f7ab1bb342547444"
AFTER
"9465d2750182acbf6dd8f61e60bb47dc1ca502be5cc3f4a71da7ad39935d1eb1"

changed /files/skills~1attack-path-analysis~1SKILL.md/size

BEFORE
8567
AFTER
8568

changed /files/skills~1deep-security-scan~1SKILL.md/sha256

BEFORE
"e3e758d1a6b96b42453fc631b58e04e851cec71318b18d53e2d1c344f6edc4db"
AFTER
"d6aed0d9b47065a1384a96d0a45c843b0cda8ba48e3d2cbe96c65357c6e55ee3"

changed /files/skills~1deep-security-scan~1SKILL.md/size

BEFORE
13815
AFTER
14096

changed /files/skills~1finding-discovery~1SKILL.md/sha256

BEFORE
"1d3a159a5ed83722ddf7369dad9f7788e283454a8ee963cef35ccd8102ea8b0e"
AFTER
"14cc797e750ca4bdd8b060c4cf96d7d32f25b132aa8f80d0a0837683fe010790"

changed /files/skills~1finding-discovery~1SKILL.md/size

BEFORE
25450
AFTER
25598

changed /files/skills~1propose-security-hardening~1references~1proposal-format.md/sha256

BEFORE
"6c0000722629098365afbf6809c3402121f90438600fb574fbc2c77aeb649ba4"
AFTER
"72b93c32f1cbe7d25a3d347d0894e7b642942e68f2d8a3c0409bc398a41b06e7"

changed /files/skills~1propose-security-hardening~1references~1proposal-format.md/size

BEFORE
25476
AFTER
25933

changed /files/skills~1security-diff-scan~1SKILL.md/sha256

BEFORE
"0a4c519ad713585876ea7eb0a8af4b59892c86746f4c69851db9ab347b7fad2f"
AFTER
"f85962d46f141227d794a25253a39232bc4e1ad756509ecd3d8769b85b735136"

changed /files/skills~1security-diff-scan~1SKILL.md/size

BEFORE
5449
AFTER
6102

changed /files/skills~1security-scan~1SKILL.md/sha256

BEFORE
"5b8f5d7debeca14c6b37e8e7ba737671362b8eb4b7f49e693c99c6bd04bc8fa0"
AFTER
"104e2f93fc965c34e91970f517a89e330d5b29dbb243dcea5d95d83f14d135cf"

changed /files/skills~1security-scan~1SKILL.md/size

BEFORE
6975
AFTER
9007

changed /files/skills~1security-scan~1references~1desktop-scan.md/sha256

BEFORE
"fc20c0a72913cf88f99879091c5b64e4161a42670e84bff29b629210006cd860"
AFTER
"62a3b3baac5773c36eb291c74c0304dd97157aff5b89dd56fa8b0fed08803389"

changed /files/skills~1security-scan~1references~1desktop-scan.md/size

BEFORE
4428
AFTER
4747

changed /files/skills~1security-scan~1references~1scan-artifacts-and-ledger.md/sha256

BEFORE
"443084c974eb80747a6dc4e092c7fca55f412ca5ab3f14e2d27dc1223cc690a4"
AFTER
"c95780a7af36fd42716f4c909bb57e68b438769b13f8d6f02ac06ffa73c838ff"

changed /files/skills~1security-scan~1references~1scan-artifacts-and-ledger.md/size

BEFORE
13628
AFTER
14716

changed /files/skills~1threat-model~1SKILL.md/sha256

BEFORE
"f45f02d9f607ec6797b730c71b15310f971403193cc56a984a1cbb1710b495ee"
AFTER
"42568e50c787c4c0bfb9495ad67e72ab6c1a951bb910ecba2c67cff8961c4d56"

changed /files/skills~1threat-model~1SKILL.md/size

BEFORE
3149
AFTER
3606

changed /files/skills~1track-findings~1SKILL.md/sha256

BEFORE
"8e3726e86ef0df509f1a635961f268e1bf04a4622a83e21c1d079f546cc61dcb"
AFTER
"a6d2f30e6d5ca7bd9d74d83017f2f5481c5829585941208a1673b82c683d292b"

changed /files/skills~1track-findings~1SKILL.md/size

BEFORE
20543
AFTER
11371

changed /files/skills~1track-findings~1references~1github-security-advisories.md/sha256

BEFORE
"f56010d265d0ce56e555dd73a92777a6d3be8f5d31bd2af7ff163423a667d10e"
AFTER
"5e583aa2f77db32e4983b8b4bb857b1c7eaebfa1f43fc833b551c96acab70d4e"

changed /files/skills~1track-findings~1references~1github-security-advisories.md/size

BEFORE
4065
AFTER
3288

changed /files/skills~1track-findings~1references~1jira.md/sha256

BEFORE
"05affbdefbfd8054d08f16e5fe15729ade440b5627a4561f8693bd97fb3a3ea2"
AFTER
"bb1f7bb6fa638f20f1caad35b04482ae33f0cdbf2ab9c617e7805575dff457a0"

changed /files/skills~1track-findings~1references~1jira.md/size

BEFORE
5995
AFTER
3027

changed /files/skills~1triage-finding~1SKILL.md/sha256

BEFORE
"70b0e771f286443ea32bbf466ad378b36f9123b4aa171ab02c6680fdbdb704f9"
AFTER
"83081950bd4b29ff37cf82d137aec164532ae56b03dbaad3f4dd1d5423e758a7"

changed /files/skills~1triage-finding~1SKILL.md/size

BEFORE
27559
AFTER
10004

changed /files/skills~1triage-finding~1agents~1openai.yaml/sha256

BEFORE
"1d53cfba14878c05745309eec3f682f70c5e8d49ecc033628cf832a5dd27c9cb"
AFTER
"4a4094210aa6360cf503f5adf7be1e590e27898f3163da1b713632d2828e8563"

changed /files/skills~1triage-finding~1agents~1openai.yaml/size

BEFORE
770
AFTER
400

changed /files/skills~1triage-finding~1references~1ticket-intake.md/sha256

BEFORE
"bd545655e0402f5f326f34191a67b6dd30c382914e9ae81b2e66ad745860e78a"
AFTER
"693b415fdfcdcf8e6d5539677ff89c79bb36995ae22916d87866ce2c25113439"

changed /files/skills~1triage-finding~1references~1ticket-intake.md/size

BEFORE
6250
AFTER
3645

changed /files/skills~1triage-finding~1references~1triage-result-contract.md/sha256

BEFORE
"321027c102cf36119376ef86f0701a4a7c6fc22e46d28fd240639aae156b134a"
AFTER
"709c1d803c001a47c884ba10ac97f8c1434c3ce06a75033778343a662b25729b"

changed /files/skills~1triage-finding~1references~1triage-result-contract.md/size

BEFORE
4411
AFTER
4447
Full snapshot data
{
  "files": {
    ".app.json": {
      "sha256": "a7bbe314059c7311e169474823e3978f267e560c3e961ba49fb94032da9843fe",
      "size": 479
    },
    ".codex-plugin/plugin.json": {
      "sha256": "527d1a0db286884729572d400b0ad9b0ff852d4a12b36228ff5fbef180f7de9d",
      "size": 1582
    },
    ".mcp.json": {
      "sha256": "7c5968060f6e848859ce02450d6e02e67029d8d39011bbda1059b19de6309ebf",
      "size": 1679
    },
    "OWNERS": {
      "sha256": "49be76d15302e4f158b3ef907e9802a955ab9c0a59a039827eab4cb6221f0e5f",
      "size": 41
    },
    "assets/logo.png": {
      "sha256": "9b9c2b09b2fa064611fb62307d321d5c2ea70cf0789f7ce34cdb0fc0d9190b3a",
      "size": 99567
    },
    "examples/completed-scan/coverage.json": {
      "sha256": "d55b9b98d48323b4659dfee6531ec83ec538f2084325412b7188bf85ad68b01b",
      "size": 467
    },
    "examples/completed-scan/findings.json": {
      "sha256": "a6dc4521d6478828224fbafc33585401a47bfeb0e56e07b5d2886e8a29937f2f",
      "size": 1844
    },
    "examples/completed-scan/report.md": {
      "sha256": "83b7c709c0b49d1500b52e61a88ea70b8423ebebec38308b1294cf094838b66f",
      "size": 4116
    },
    "examples/completed-scan/scan-manifest.json": {
      "sha256": "d245ae9fc62a676293c6821e562d1a2d7d59db12d5b8dc99b85f7c5f1462fe00",
      "size": 1245
    },
    "integrity.json": {
      "sha256": "6b342df66fb9b5ce7fc8c9de0e974b59d21340cdc06167df46d1a19bd4ea9441",
      "size": 17465
    },
    "mcp/helpers.mjs": {
      "sha256": "c272f3761e5c405619b26e4d0fd621df5ac189f2ee1bb32b975480e9835f9ca8",
      "size": 1097
    },
    "mcp/helpers.mjs.br.part-000": {
      "sha256": "6299f73c92c4f924bffc6b1bed001480603473967006b3e75941bc49a21f18d5",
      "size": 63398
    },
    "mcp/native/COPYRIGHT-library.html": {
      "sha256": "0a65bb747c49c7bb816cbc7188319bd6e4e8d08091c1190b8a3c0971c47968ed",
      "size": 279302
    },
    "mcp/native/THIRD_PARTY_NOTICES.txt": {
      "sha256": "16ac63908855f5bb7eab864923cc1855b406bd7051dfcd70d7e69684f6c69955",
      "size": 308404
    },
    "mcp/native/darwin-arm64/unix.node": {
      "sha256": "093e87d8de3d2676fc9a2a1430f571bf4fc970b2bbef4c7ded0bf8924e45f473",
      "size": 354352
    },
    "mcp/native/darwin-x64/unix.node": {
      "sha256": "57c45d212d35ce7599afc7b9b21693e18443753231bad1fb5146a4e224db68ec",
      "size": 343296
    },
    "mcp/native/licenses/Apache-2.0.txt": {
      "sha256": "074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff",
      "size": 10280
    },
    "mcp/native/licenses/BSD-2-Clause.txt": {
      "sha256": "f32fb3b417a194167cfad068223fc975ba96c5960513a10f66a3c28720aec1df",
      "size": 1267
    },
    "mcp/native/licenses/MIT.txt": {
      "sha256": "b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0",
      "size": 1078
    },
    "mcp/native/licenses/Unicode-3.0.txt": {
      "sha256": "f5062c9a188d81dfe66b56db4182dcf9e4b17c0d9b0d311a8e20b3a1b075c443",
      "size": 1995
    },
    "mcp/native/linux-arm64-gnu/unix.node": {
      "sha256": "6b352aaaac21f9ad9b772481429ba7bc8433a0e44ca5ffe433ad47289c3007f6",
      "size": 465088
    },
    "mcp/native/linux-arm64-musl/unix.node": {
      "sha256": "3a55ec4f42202c58a29654701c31a8deab386969d14e2ab85f23e81345b4fedb",
      "size": 332456
    },
    "mcp/native/linux-x64-gnu/unix.node": {
      "sha256": "ed2061078400010852a9f8d3ecdf7bc0a008d13adc9e9d5c6954f5cb686a2a2a",
      "size": 365352
    },
    "mcp/native/linux-x64-musl/unix.node": {
      "sha256": "c2e4c07ac6dde175464c6315937652465b200e5f28590898663523beaf4e9ab6",
      "size": 369304
    },
    "mcp/native/win32-arm64/windows.node": {
      "sha256": "19f78e88fd8934038201c26b044123bc05db74f5d7065df10a32583ccbcf7a55",
      "size": 419840
    },
    "mcp/native/win32-x64/windows.node": {
      "sha256": "5aed21edb5fe06efac49d9bd3899d6242f60b1bfac2b710b967a76c924d98298",
      "size": 453120
    },
    "mcp/server.mjs": {
      "sha256": "c58624aa4c4bd3efbb67601c9c2e98759f167f608320a6cd342814b2f2e6a636",
      "size": 1096
    },
    "mcp/server.mjs.br.part-000": {
      "sha256": "7ef9c7387b405b51e2aec793a86696bf793506a8f34155199035aa515b3a415a",
      "size": 140000
    },
    "mcp/server.mjs.br.part-001": {
      "sha256": "94d6412009e284ecbc17bd14d07e0116d8e74e572a62198912ac5a03f95a50c2",
      "size": 126028
    },
    "preflight/capability-profiles.toml": {
      "sha256": "543a0f6a0e81cbbac1fe43e2e3d44d6163a6792384ab9c768ac519295d9ec8a1",
      "size": 2350
    },
    "references/artifact-storage.md": {
      "sha256": "ccce9c76f5d044fbce333d6129fbb55eb63f512c15c2ebeeca405e296f38325b",
      "size": 7632
    },
    "references/config-preflight.md": {
      "sha256": "5894f9fd7efcae7034404a2c062ee3d7417520753be16ddcdb3acf5b884f048d",
      "size": 19162
    },
    "references/core-scan.md": {
      "sha256": "9c0eca53147383a89cd345eac7791ea75b954f7f083448ef25019d8f9ba30afe",
      "size": 22978
    },
    "references/desktop-config-preflight.md": {
      "sha256": "d03e7c604a0459509a4a3238289c3ad0725811e8b11fb7d0044ce8bcee6a7a26",
      "size": 1587
    },
    "references/final-report.md": {
      "sha256": "9c34375584c8ae76959b0eae038b86b2b8ed7874517c249fcae8715314c76298",
      "size": 29931
    },
    "references/finding-detail-fields.md": {
      "sha256": "9e4dcc5633ca1f31291688606e4ec0d56067ed485e748ac9e7d433984f9ab451",
      "size": 11920
    },
    "references/sarif-adapter.md": {
      "sha256": "32725cc851d998c9b33f0232c435d720383c86bc13526b04d356b85d08101cc5",
      "size": 2341
    },
    "references/scan-artifacts.md": {
      "sha256": "eecdccac30b8ad65bb702dc9ac8d9718dcb0b95532a94a61195268d22bdf504b",
      "size": 11243
    },
    "references/scan-contract.md": {
      "sha256": "b362636c29d1e8b18053481abe2f97d7bb099a97040f5b63aa2702a41c1d62c8",
      "size": 14537
    },
    "references/scan-prologue.md": {
      "sha256": "1d23ab6db651134b54307cdea873ffa711a83458bda563f1e126f35e83b278e9",
      "size": 4812
    },
    "references/security-guidance.md": {
      "sha256": "a0f49ad3bd09fa6180a100d81326b75bf87a2d49893ca9e37b64ab151c759a80",
      "size": 1801
    },
    "references/static-finding-assessment.md": {
      "sha256": "78de933caef1c8d971dab83fbb71877602a1197a52eaa7a59d6ff7bc399d0bc7",
      "size": 3949
    },
    "references/threat-model.md": {
      "sha256": "f3dd2d05d7f0c61b13d29a78e810b367772fe22d5ac58adbbc00080a9495706e",
      "size": 15203
    },
    "schemas/coverage.schema.json": {
      "sha256": "6ac14e659e884e9582d8bd9ba480f6364ca17309e1a3dee9469ab025a3380b11",
      "size": 5198
    },
    "schemas/definitions/artifact-common.schema.json": {
      "sha256": "8187236867a2397515571d937deac92dcbf23d3db5e330d32b1f24aced4b9abc",
      "size": 1065
    },
    "schemas/definitions/discovery-candidate.schema.json": {
      "sha256": "b0cf54fc1ae1947db0f6a6f73e17d1124d528226de763fb89489d05fd612331b",
      "size": 2951
    },
    "schemas/findings.schema.json": {
      "sha256": "a480337cc0fa4c48c44fc7be17c6c4348767815570775cda80f2aaf797b8e56c",
      "size": 19167
    },
    "schemas/patch-risk-assessment.schema.json": {
      "sha256": "536492481c5b67910ce8d3754d61c7d5a44c5cb78fe3f081e8f5d1620d6ae9c9",
      "size": 6854
    },
    "schemas/scan-manifest.schema.json": {
      "sha256": "c621d4136ac81741ac8da1960c7f5e92ade22c0b5490e378ea4339b09e45b6f3",
      "size": 9513
    },
    "schemas/tools/candidate-attack-paths.schema.json": {
      "sha256": "f6fcb643b4b975466c4717356a0768b0572812cd832578d8714557f5e3d16935",
      "size": 6055
    },
    "schemas/tools/candidate-validations.schema.json": {
      "sha256": "c871eb3462b7873c1774e6aade62acdd570636e43c0ac3c7b41b7041ca5627e9",
      "size": 4916
    },
    "schemas/tools/deep-reducer.schema.json": {
      "sha256": "5f0c25f826445251415986097c41127018d19b6c690cd99720fd9365893b0028",
      "size": 2221
    },
    "schemas/tools/discovery-candidates.schema.json": {
      "sha256": "dcd2031d64015c69b415c66e57d6e90cbdef91152a445a6f409c14d80153afa6",
      "size": 2473
    },
    "schemas/tools/review-items.schema.json": {
      "sha256": "acc4f4909446a62811728dd1d6581456053da8d26f774b7af208a1856f86ac5b",
      "size": 2250
    },
    "schemas/tools/scan-draft.schema.json": {
      "sha256": "151f9dffc83e1c5b4f0a7fba20b90fb3196fdaa9719180eb0009ba2b50f75d48",
      "size": 23475
    },
    "schemas/tools/worker-threat-model.schema.json": {
      "sha256": "a00f875918794661316a033bd2d03426d61ab1e41d4a36c548c07525dcfde478",
      "size": 435
    },
    "scripts/config_preflight.py": {
      "sha256": "f4d4103650977d078b71f49da6b1e287b6db2f2b7b5fb08e943722a6d869354e",
      "size": 35176
    },
    "scripts/deep_scan_config.py": {
      "sha256": "44229bc7b2e7f4c7654e2e9ed91650a28630eeb00fcc38358f19d69739771f8c",
      "size": 5042
    },
    "scripts/deep_scan_defaults.json": {
      "sha256": "89bdb96ff721f0f1d509f3c5c83f5fc89022f14406d0a84df781b7e0e42cde66",
      "size": 143
    },
    "scripts/deep_scan_workbench.py": {
      "sha256": "5012b337503f44e086fbf021be9db8340f8ba992fe5169c5534752e900947cf5",
      "size": 84709
    },
    "scripts/filesystem_identity.py": {
      "sha256": "5298f2012b6e48ab1106837a5440ef2b3966383e2321507a47593010bb4dd01f",
      "size": 893
    },
    "scripts/finalize_scan_contract.py": {
      "sha256": "3fa92d8e2ccadb032cf458f0fde444785e8ee5ced0abe6346c285b07ef153e27",
      "size": 133770
    },
    "scripts/finding_preview.py": {
      "sha256": "ebdf8e7b7e465c0937e73e945e3593d659b2f15ff1db08d0b4a2d3b106174c4d",
      "size": 16089
    },
    "scripts/generate_in_scope_files.py": {
      "sha256": "0b420d786b17e4f91451b5b686fdb4d6beee3913eebb952f8fcbf220aec6230e",
      "size": 11646
    },
    "scripts/generate_rank_input.py": {
      "sha256": "e7c0bddbdb7e9236f7431d84b86be4c3fc202daa8d1306d6baa930110635b671",
      "size": 17899
    },
    "scripts/launch_codex_security_mcp": {
      "sha256": "56794e736a1e8f588f9959a2e707ea3ed9bc7d4d2b796c132e91d5fbed59e600",
      "size": 1793
    },
    "scripts/launch_codex_security_mcp.cmd": {
      "sha256": "2539c0914bb130d76629e8176ac93762cd0051677ac2c525e7ba524b0b828f80",
      "size": 2582
    },
    "scripts/rank_preview.py": {
      "sha256": "627d0b89c9d4436162ab9bcb80f7aa562605695e784dc612307c7eda6fef3a7f",
      "size": 34726
    },
    "scripts/report_projection.py": {
      "sha256": "1b24978a3cdb3c336ac27a2b39fb35457bade13a4aad0782710fe38ac39965cf",
      "size": 41098
    },
    "scripts/reserved_artifact_paths.json": {
      "sha256": "58ba1cf42437ac3af65c85a66eeff46f22572e45081fa2620f4b241173efcc50",
      "size": 186
    },
    "scripts/snapshot_sqlite.py": {
      "sha256": "9e0be851702f0962070ece6e0e2959e3955fc99126f68452d175fc22bc38423b",
      "size": 1084
    },
    "scripts/threat_model_projection.py": {
      "sha256": "32e4d5cd370a49cdc5658c3ded3d4d63ba7f41a44b34b67da87321048d088111",
      "size": 6711
    },
    "scripts/validate_scan_contract.py": {
      "sha256": "38a5eb0126ea06b22eb82c5829e4ffcbebd2c1e55f99634454622fedd7b9e1a0",
      "size": 3803
    },
    "scripts/validate_tracking_source.py": {
      "sha256": "802fb3062456128c7adceb6048183811120255b222018e123e10c5ba0caa4bdc",
      "size": 2813
    },
    "scripts/windows_scan_local_files.py": {
      "sha256": "c26276695435d134c1354048c3c74c3c6b0ed203158a93b63b67ef0b6bfab843",
      "size": 24919
    },
    "scripts/workbench/__init__.py": {
      "sha256": "3635fffd60f51d5a5d889e20c91df803c667f8b1b29cbcf68c96cb2e0a9c33ed",
      "size": 58
    },
    "scripts/workbench/handoff.py": {
      "sha256": "83606054cc47064514a612d8ce16353cce669fe07b61852d73bc0c94b75c8ee5",
      "size": 8914
    },
    "scripts/workbench/storage.py": {
      "sha256": "5e389073520fb7668defd433228befd9e7b9c8309c1b6024bffd6213d59f1d88",
      "size": 973
    },
    "scripts/workbench_cli.py": {
      "sha256": "959e951fa5daf326049ee61bd0773999be92600b65a9062e56b4f62fc4e2aa55",
      "size": 21260
    },
    "scripts/workbench_constants.py": {
      "sha256": "0ff12b11b2ae9f71c1de6cff0afc234b035379bb1193f1ca9a56ddeb7aa32cf7",
      "size": 2187
    },
    "scripts/workbench_dashboard.py": {
      "sha256": "5770b77ff551cde46c22fae9239ef8ce28254320da77876e0b936dc35b829967",
      "size": 5990
    },
    "scripts/workbench_db.py": {
      "sha256": "94bf11d7d74202ae7efbdd4f70a036393d4ba013ffc2ada44172a59be41f97f2",
      "size": 146186
    },
    "scripts/workbench_feedback.py": {
      "sha256": "675bc5897444a39950297f74353fa6fd296f73d3a5ac87d294647ae8425151dd",
      "size": 4120
    },
    "scripts/workbench_finding_index.py": {
      "sha256": "4618f994786fa2300e78592fa2868bc0a585368127027c1940a0e504b0254265",
      "size": 3938
    },
    "scripts/workbench_finding_workflows.py": {
      "sha256": "de44bfc28c0a4d2021643a50d5a42408f4853bb4a21cf9d1f4ffb43a695b06ce",
      "size": 8754
    },
    "scripts/workbench_findings.py": {
      "sha256": "b78e2ca68f29cf002b6215985225e1b3996c8edd08601569945405d42c33b301",
      "size": 8881
    },
    "scripts/workbench_native_indexes.py": {
      "sha256": "114dbf1e4050302948c4b8f24d69454f127d128e3bc35e34a5f3ec9dd3b187cd",
      "size": 9870
    },
    "scripts/workbench_progress.py": {
      "sha256": "42c7d0b8b4d253095e1062922cd9e59b10b966ac314bef092cb78cad2217b967",
      "size": 13296
    },
    "scripts/workbench_publication.py": {
      "sha256": "9312a2962b797ed355d6b05fb375da6325aeb5c5ead5d52f2fb7114ef3ad9635",
      "size": 20346
    },
    "scripts/workbench_remediation.py": {
      "sha256": "96585805c4956917bb473c3b60493a3c15daa2028b074b756e36358a9ac76525",
      "size": 7281
    },
    "scripts/workbench_saved_results.py": {
      "sha256": "436b23a4d169006437985bb352bfd792da9e465c4c7506dce31bfd5e78524c6d",
      "size": 119202
    },
    "scripts/workbench_scan_history.py": {
      "sha256": "920a4c6448c4ee87500e4488fdfa5a294dfa0696f94f55f2e2cbe544e5acf60f",
      "size": 48177
    },
    "scripts/workbench_scan_start.py": {
      "sha256": "647e54ce40a7dc5b17d6abe547406f22ca0cb4eee628938b4c21629eb8e73c35",
      "size": 7963
    },
    "scripts/workbench_scan_usage.py": {
      "sha256": "21d845f8967c9206351e54f94fd2b4bbfe834434e35b642992d7a829b76cd94e",
      "size": 22094
    },
    "scripts/workbench_schema.py": {
      "sha256": "25ef8550ded14785fa47b19f1fa358422d8883affc94641c84e68c172e800cc0",
      "size": 51783
    },
    "scripts/workbench_severity.py": {
      "sha256": "ec74f540595e8052e295846303f31988329e0dc68fcebdd92572673555b1c99d",
      "size": 4625
    },
    "scripts/workbench_source_excerpt.py": {
      "sha256": "5681d4a5c547404f687c644f54432c10e0770b6ba4c1e6be321664579a252a24",
      "size": 3210
    },
    "scripts/workbench_target.py": {
      "sha256": "08ed149295b9347ac37f2b8523c822a35f165283422c7644dabecfa236b81b72",
      "size": 32451
    },
    "scripts/workbench_target_state.py": {
      "sha256": "9c133f67397ab4b90ce1275edc09a7e6d05900236209bc72e8f113b9f0cde287",
      "size": 1920
    },
    "scripts/workbench_validation.py": {
      "sha256": "11259b8ce1e135c5a86677964f29e6331e65e5fd80a1931c80f7fac507cccb13",
      "size": 6547
    },
    "skills/assess-patch-risk/SKILL.md": {
      "sha256": "c3b62a8c1d6135945a055134f50329047bb908972fc7b320a592edbc3d38b419",
      "size": 9511
    },
    "skills/assess-patch-risk/agents/openai.yaml": {
      "sha256": "fec61f321e18022579cac25ffb33286f48cc316172a0b8d502f2bf4faecba025",
      "size": 274
    },
    "skills/assess-patch-risk/references/risk-rubric.md": {
      "sha256": "078a71175191580791685808980cb1815dc97265cfee8c0ddad1f4d3b8203b4a",
      "size": 5049
    },
    "skills/attack-path-analysis/SKILL.md": {
      "sha256": "9465d2750182acbf6dd8f61e60bb47dc1ca502be5cc3f4a71da7ad39935d1eb1",
      "size": 8568
    },
    "skills/attack-path-analysis/agents/openai.yaml": {
      "sha256": "d9355cbcf0bc81098c9e837d9d4e8644e598410d052a862ab11cf91ef9ccb633",
      "size": 261
    },
    "skills/attack-path-analysis/references/attack-path-facts.md": {
      "sha256": "8c8f1abb46ddf045d76908df78e3a273da8d61aaa0f37fc992534a3ddfe42430",
      "size": 2882
    },
    "skills/attack-path-analysis/references/severity-policy.md": {
      "sha256": "44719bb96a1065df7683a4f7968145efd3b3f365ec95c22a4bed5b20b5d8a0a1",
      "size": 15288
    },
    "skills/deep-security-scan/SKILL.md": {
      "sha256": "d6aed0d9b47065a1384a96d0a45c843b0cda8ba48e3d2cbe96c65357c6e55ee3",
      "size": 14096
    },
    "skills/deep-security-scan/agents/openai.yaml": {
      "sha256": "3f8d781bf372b0d053a017eabaebf947d5d48d9be2b82cb67da211128924c3f1",
      "size": 192
    },
    "skills/define-security-policy/SKILL.md": {
      "sha256": "2817e1a03980a7ebea478c8d9e6c622e3c6205123e767a1e32f0204c06729cc7",
      "size": 6256
    },
    "skills/define-security-policy/agents/openai.yaml": {
      "sha256": "8c40b0f8bd1eb7feabdcea69f12c2a7d18e492e96893c0702507ffd808b8e2b4",
      "size": 210
    },
    "skills/finding-discovery/SKILL.md": {
      "sha256": "14cc797e750ca4bdd8b060c4cf96d7d32f25b132aa8f80d0a0837683fe010790",
      "size": 25598
    },
    "skills/finding-discovery/agents/openai.yaml": {
      "sha256": "c044dd83a7624b90ed19f11adc899202685ce64e32502f1d46225f69f9a2c962",
      "size": 220
    },
    "skills/fix-finding/SKILL.md": {
      "sha256": "ba7311f43b2c0742da0d5c6bdb2f0b26601c09cc59747cf8c62a09f8d0bb86d2",
      "size": 9477
    },
    "skills/fix-finding/agents/openai.yaml": {
      "sha256": "5dac397bf3a17e45283691b656179eb56d19e34881af380f098d7c455bce52d7",
      "size": 225
    },
    "skills/propose-security-hardening/SKILL.md": {
      "sha256": "ad07d88da7c2bc9e551849cdde686b0daad380ed0aac798e6c06444681d75959",
      "size": 22791
    },
    "skills/propose-security-hardening/agents/openai.yaml": {
      "sha256": "1bcab19d48eb4b768c0dc435bd2e738f1471c2345cce5865bb31bdca5be0b134",
      "size": 293
    },
    "skills/propose-security-hardening/references/proposal-format.md": {
      "sha256": "72b93c32f1cbe7d25a3d347d0894e7b642942e68f2d8a3c0409bc398a41b06e7",
      "size": 25933
    },
    "skills/security-diff-scan/SKILL.md": {
      "sha256": "f85962d46f141227d794a25253a39232bc4e1ad756509ecd3d8769b85b735136",
      "size": 6102
    },
    "skills/security-diff-scan/agents/openai.yaml": {
      "sha256": "f3d31e09befbb8532b53fdccb63a34c0b894a7fb96215b40e84647cd9f8e1b50",
      "size": 284
    },
    "skills/security-scan/SKILL.md": {
      "sha256": "104e2f93fc965c34e91970f517a89e330d5b29dbb243dcea5d95d83f14d135cf",
      "size": 9007
    },
    "skills/security-scan/agents/openai.yaml": {
      "sha256": "5952ae0b0f7d378b7e6ab3fe793335a0e26e5d15bd2d7fdd14ecdd795607ceb6",
      "size": 271
    },
    "skills/security-scan/references/desktop-scan.md": {
      "sha256": "62a3b3baac5773c36eb291c74c0304dd97157aff5b89dd56fa8b0fed08803389",
      "size": 4747
    },
    "skills/security-scan/references/scan-artifacts-and-ledger.md": {
      "sha256": "c95780a7af36fd42716f4c909bb57e68b438769b13f8d6f02ac06ffa73c838ff",
      "size": 14716
    },
    "skills/threat-model/SKILL.md": {
      "sha256": "42568e50c787c4c0bfb9495ad67e72ab6c1a951bb910ecba2c67cff8961c4d56",
      "size": 3606
    },
    "skills/threat-model/agents/openai.yaml": {
      "sha256": "a3533478c2548248ef07c9afbbe0a7451f1fa1882883b4def0906d8113b7dd6c",
      "size": 171
    },
    "skills/track-findings/SKILL.md": {
      "sha256": "a6d2f30e6d5ca7bd9d74d83017f2f5481c5829585941208a1673b82c683d292b",
      "size": 11371
    },
    "skills/track-findings/agents/openai.yaml": {
      "sha256": "39e73c0003afc8bd9d3cf53c027d8274e724dc41f46e3cd6863ad34bb4de4200",
      "size": 279
    },
    "skills/track-findings/references/github-security-advisories.md": {
      "sha256": "5e583aa2f77db32e4983b8b4bb857b1c7eaebfa1f43fc833b551c96acab70d4e",
      "size": 3288
    },
    "skills/track-findings/references/jira.md": {
      "sha256": "bb1f7bb6fa638f20f1caad35b04482ae33f0cdbf2ab9c617e7805575dff457a0",
      "size": 3027
    },
    "skills/triage-finding/SKILL.md": {
      "sha256": "83081950bd4b29ff37cf82d137aec164532ae56b03dbaad3f4dd1d5423e758a7",
      "size": 10004
    },
    "skills/triage-finding/agents/openai.yaml": {
      "sha256": "4a4094210aa6360cf503f5adf7be1e590e27898f3163da1b713632d2828e8563",
      "size": 400
    },
    "skills/triage-finding/references/github-rest-intake.md": {
      "sha256": "f026bef041ca92f3d9b47a131794aa54e98cf8d7716fae3f71ab53e8ab09c79f",
      "size": 9314
    },
    "skills/triage-finding/references/ticket-intake.md": {
      "sha256": "693b415fdfcdcf8e6d5539677ff89c79bb36995ae22916d87866ce2c25113439",
      "size": 3645
    },
    "skills/triage-finding/references/triage-result-contract.md": {
      "sha256": "709c1d803c001a47c884ba10ac97f8c1434c3ce06a75033778343a662b25729b",
      "size": 4447
    },
    "skills/validation/SKILL.md": {
      "sha256": "3c4660f79b12a92728e6bb25027064c3a911d0960d39aeafc8fda9e42ce517a5",
      "size": 12845
    },
    "skills/validation/agents/openai.yaml": {
      "sha256": "7516bcd4748b55566694284d7d953a7db0c94d5a2123895fabb36d7d9d7d74e4",
      "size": 183
    },
    "skills/validation/references/validation-guidance.md": {
      "sha256": "42f7a9d85b78d0deb5e6eda82b8c59ed101c40d36065833b9cf39fe50699e255",
      "size": 26823
    },
    "skills/verify-fix/SKILL.md": {
      "sha256": "3d4530484c7e4c18b6afe7c6527247e96517dce1ebd1bda9d349cd367ecb0ae3",
      "size": 3025
    },
    "skills/verify-fix/agents/openai.yaml": {
      "sha256": "ac35fc4704cde35b6bf103e16a16d0a44f2b9c2671e1634292cf285905cf40f3",
      "size": 235
    },
    "skills/vulnerability-writeup/SKILL.md": {
      "sha256": "d08582d9963ce3bfbc3fe81b6e85a01e6fe306713d7951bfd8dd2fbc3df75de4",
      "size": 27640
    },
    "skills/vulnerability-writeup/agents/openai.yaml": {
      "sha256": "7987566daac59e3dd6c5211fe020f71fc4c862843c231d10c28a45697d680f9e",
      "size": 290
    },
    "skills/vulnerability-writeup/references/report-format.md": {
      "sha256": "60568466f7057562d7359dc3a396632c760c147582603896d7e569a77487d0b1",
      "size": 14682
    }
  }
}

SHA-256 of public snapshot: 8e8a2a0d24309bd11d1c54794bd1027470e5f7bc759784322ed68d7643d69ec8