@hunter-harness/workflow-harness 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-archive/reference.md +2 -8
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +2 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +5 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/checklist.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-submit/checklist.md +30 -6
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-test/checklist.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/pitfalls.md +16 -0
- package/harness/bundles/general/claude-code/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/general/claude-code/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/general/claude-code/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/general/claude-code/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/general/claude-code/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +31 -14
- package/harness/bundles/general/claude-code/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/general/claude-code/scripts/harness_deploy.py +179 -4
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +269 -3
- package/harness/bundles/general/claude-code/scripts/harness_preflight.py +72 -382
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +742 -0
- package/harness/bundles/general/claude-code/scripts/harness_service.py +99 -2
- package/harness/bundles/general/claude-code/scripts/harness_state.py +179 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/reference.md +2 -8
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +2 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +5 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-submit/checklist.md +30 -6
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-test/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/pitfalls.md +16 -0
- package/harness/bundles/general/codebuddy/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/general/codebuddy/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/general/codebuddy/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/general/codebuddy/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/general/codebuddy/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +31 -14
- package/harness/bundles/general/codebuddy/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/general/codebuddy/scripts/harness_deploy.py +179 -4
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +269 -3
- package/harness/bundles/general/codebuddy/scripts/harness_preflight.py +72 -382
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +742 -0
- package/harness/bundles/general/codebuddy/scripts/harness_service.py +99 -2
- package/harness/bundles/general/codebuddy/scripts/harness_state.py +179 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-archive/reference.md +2 -8
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +2 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +5 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-submit/checklist.md +30 -6
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-test/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-test/pitfalls.md +16 -0
- package/harness/bundles/general/codex/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/general/codex/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/general/codex/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/general/codex/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/general/codex/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/general/codex/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/general/codex/scripts/harness_archive.py +31 -14
- package/harness/bundles/general/codex/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/general/codex/scripts/harness_deploy.py +179 -4
- package/harness/bundles/general/codex/scripts/harness_ledger.py +269 -3
- package/harness/bundles/general/codex/scripts/harness_preflight.py +72 -382
- package/harness/bundles/general/codex/scripts/harness_profile.py +742 -0
- package/harness/bundles/general/codex/scripts/harness_service.py +99 -2
- package/harness/bundles/general/codex/scripts/harness_state.py +179 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-archive/reference.md +2 -8
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +2 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +5 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-submit/checklist.md +30 -6
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-test/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-test/pitfalls.md +16 -0
- package/harness/bundles/general/cursor/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/general/cursor/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/general/cursor/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/general/cursor/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/general/cursor/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/general/cursor/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/general/cursor/scripts/harness_archive.py +31 -14
- package/harness/bundles/general/cursor/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/general/cursor/scripts/harness_deploy.py +179 -4
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +269 -3
- package/harness/bundles/general/cursor/scripts/harness_preflight.py +72 -382
- package/harness/bundles/general/cursor/scripts/harness_profile.py +742 -0
- package/harness/bundles/general/cursor/scripts/harness_service.py +99 -2
- package/harness/bundles/general/cursor/scripts/harness_state.py +179 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/reference.md +2 -8
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +3 -3
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +2 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +5 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/claude-code/harness-run/checklist.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +3 -3
- package/harness/bundles/java/claude-code/harness-submit/checklist.md +30 -6
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +5 -5
- package/harness/bundles/java/claude-code/harness-test/pitfalls.md +16 -0
- package/harness/bundles/java/claude-code/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/java/claude-code/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/java/claude-code/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/java/claude-code/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/java/claude-code/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +31 -14
- package/harness/bundles/java/claude-code/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/java/claude-code/scripts/harness_deploy.py +179 -4
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +269 -3
- package/harness/bundles/java/claude-code/scripts/harness_preflight.py +72 -382
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +742 -0
- package/harness/bundles/java/claude-code/scripts/harness_service.py +99 -2
- package/harness/bundles/java/claude-code/scripts/harness_state.py +179 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/reference.md +2 -8
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +3 -3
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +2 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +5 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/codebuddy/harness-run/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +3 -3
- package/harness/bundles/java/codebuddy/harness-submit/checklist.md +30 -6
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +5 -5
- package/harness/bundles/java/codebuddy/harness-test/pitfalls.md +16 -0
- package/harness/bundles/java/codebuddy/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/java/codebuddy/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/java/codebuddy/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/java/codebuddy/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/java/codebuddy/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +31 -14
- package/harness/bundles/java/codebuddy/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/java/codebuddy/scripts/harness_deploy.py +179 -4
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +269 -3
- package/harness/bundles/java/codebuddy/scripts/harness_preflight.py +72 -382
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +742 -0
- package/harness/bundles/java/codebuddy/scripts/harness_service.py +99 -2
- package/harness/bundles/java/codebuddy/scripts/harness_state.py +179 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/reference.md +2 -8
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +3 -3
- package/harness/bundles/java/codex/harness-plan/SKILL.md +2 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +5 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/codex/harness-run/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +3 -3
- package/harness/bundles/java/codex/harness-submit/checklist.md +30 -6
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +5 -5
- package/harness/bundles/java/codex/harness-test/pitfalls.md +16 -0
- package/harness/bundles/java/codex/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/java/codex/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/java/codex/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/java/codex/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/java/codex/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/java/codex/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/java/codex/scripts/harness_archive.py +31 -14
- package/harness/bundles/java/codex/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/java/codex/scripts/harness_deploy.py +179 -4
- package/harness/bundles/java/codex/scripts/harness_ledger.py +269 -3
- package/harness/bundles/java/codex/scripts/harness_preflight.py +72 -382
- package/harness/bundles/java/codex/scripts/harness_profile.py +742 -0
- package/harness/bundles/java/codex/scripts/harness_service.py +99 -2
- package/harness/bundles/java/codex/scripts/harness_state.py +179 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/reference.md +2 -8
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +2 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +474 -122
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +3 -3
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +2 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +5 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +4 -4
- package/harness/bundles/java/cursor/harness-run/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +3 -3
- package/harness/bundles/java/cursor/harness-submit/checklist.md +30 -6
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +5 -5
- package/harness/bundles/java/cursor/harness-test/pitfalls.md +16 -0
- package/harness/bundles/java/cursor/harness-test/scripts/runtime-helpers.mjs +216 -0
- package/harness/bundles/java/cursor/protocols/archive-report-protocol.md +1 -1
- package/harness/bundles/java/cursor/protocols/ledger-protocol.md +55 -2
- package/harness/bundles/java/cursor/protocols/report-pipeline-protocol.md +1 -8
- package/harness/bundles/java/cursor/protocols/sensitive-info-protocol.md +13 -0
- package/harness/bundles/java/cursor/protocols/state-layout-protocol.md +24 -0
- package/harness/bundles/java/cursor/scripts/harness_archive.py +31 -14
- package/harness/bundles/java/cursor/scripts/harness_check_gate.py +103 -0
- package/harness/bundles/java/cursor/scripts/harness_deploy.py +179 -4
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +269 -3
- package/harness/bundles/java/cursor/scripts/harness_preflight.py +72 -382
- package/harness/bundles/java/cursor/scripts/harness_profile.py +742 -0
- package/harness/bundles/java/cursor/scripts/harness_service.py +99 -2
- package/harness/bundles/java/cursor/scripts/harness_state.py +179 -0
- package/harness/manifests/general/claude-code.json +44 -28
- package/harness/manifests/general/codebuddy.json +44 -28
- package/harness/manifests/general/codex.json +44 -28
- package/harness/manifests/general/cursor.json +44 -28
- package/harness/manifests/java/claude-code.json +45 -29
- package/harness/manifests/java/codebuddy.json +45 -29
- package/harness/manifests/java/codex.json +45 -29
- package/harness/manifests/java/cursor.json +45 -29
- package/hunter-workflow-family.json +2 -2
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ import argparse
|
|
|
15
15
|
import datetime as dt
|
|
16
16
|
import hashlib
|
|
17
17
|
import json
|
|
18
|
+
import subprocess
|
|
18
19
|
import sys
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from typing import Any
|
|
@@ -26,7 +27,7 @@ if hasattr(sys.stderr, "reconfigure"):
|
|
|
26
27
|
sys.stderr.reconfigure(encoding="utf-8")
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
VERIFICATIONS = frozenset({"compile", "unitTest", "unitTestFull", "apiTest", "install"})
|
|
30
|
+
VERIFICATIONS = frozenset({"compile", "unitTest", "unitTestFull", "apiTest", "install", "package"})
|
|
30
31
|
STATUS_MAP = {
|
|
31
32
|
"ok": "OK",
|
|
32
33
|
"OK": "OK",
|
|
@@ -37,6 +38,24 @@ STATUS_MAP = {
|
|
|
37
38
|
}
|
|
38
39
|
BROAD_SCOPES = frozenset({"module", "module-am", "full"})
|
|
39
40
|
|
|
41
|
+
# --- Ledger v2 (cluster 2) ---
|
|
42
|
+
LEDGER_VERSION = "harness-ledger-2"
|
|
43
|
+
DIFF_HASH_VERSION = "content-changeset-1"
|
|
44
|
+
# Coverage lattice: a recorded verification's coverage rank must meet the
|
|
45
|
+
# verification's required rank. Prevents incremental evidence from satisfying
|
|
46
|
+
# a module/full gate (UT-015 / API-005).
|
|
47
|
+
COVERAGE_RANK = {"incremental": 0, "module": 1, "module-am": 2, "full": 3}
|
|
48
|
+
REQUIRED_COVERAGE = {
|
|
49
|
+
"unitTest": 0, # incremental suffices (scope checked separately)
|
|
50
|
+
"unitTestFull": 1, # module or broader
|
|
51
|
+
"compile": 1,
|
|
52
|
+
"apiTest": 1,
|
|
53
|
+
"install": 2, # module-am or broader
|
|
54
|
+
"package": 2,
|
|
55
|
+
}
|
|
56
|
+
# git empty-tree object id (used as base fallback when no commit exists).
|
|
57
|
+
_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|
|
58
|
+
|
|
40
59
|
|
|
41
60
|
def now_iso() -> str:
|
|
42
61
|
return dt.datetime.now().astimezone().isoformat(timespec="milliseconds")
|
|
@@ -51,6 +70,8 @@ def emit_json(payload: dict[str, Any], *, as_json: bool) -> None:
|
|
|
51
70
|
reuse = payload.get("reuse")
|
|
52
71
|
if reuse is not None:
|
|
53
72
|
sys.stdout.write(f"reuse={reuse} reason={payload.get('reason')}\n")
|
|
73
|
+
elif "diffHash" in payload:
|
|
74
|
+
sys.stdout.write(f"{payload['diffHash']}\n")
|
|
54
75
|
elif "inputsHash" in payload:
|
|
55
76
|
sys.stdout.write(f"{payload['inputsHash']}\n")
|
|
56
77
|
else:
|
|
@@ -111,6 +132,95 @@ def compute_inputs_hash(file_paths: list[str]) -> tuple[str, list[str]]:
|
|
|
111
132
|
return f"sha256:{combined.hexdigest()}", resolved_files_sorted
|
|
112
133
|
|
|
113
134
|
|
|
135
|
+
def _git_bytes(args: list[str], cwd: Path) -> bytes:
|
|
136
|
+
"""Run git, return raw stdout bytes. Raises RuntimeError on non-zero exit."""
|
|
137
|
+
proc = subprocess.run(["git", *args], cwd=str(cwd), capture_output=True)
|
|
138
|
+
if proc.returncode != 0:
|
|
139
|
+
msg = proc.stderr.decode("utf-8", "replace").strip()
|
|
140
|
+
raise RuntimeError(f"git {' '.join(args)} failed: {msg}")
|
|
141
|
+
return proc.stdout
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _root_commit(repo_root: Path) -> str | None:
|
|
145
|
+
out = _git_bytes(["rev-list", "--max-parents=0", "HEAD"], repo_root)
|
|
146
|
+
lines = [ln for ln in out.decode("utf-8", "replace").splitlines() if ln.strip()]
|
|
147
|
+
return lines[0] if lines else None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _changed_paths(repo_root: Path, base: str | None) -> tuple[str, list[str]]:
|
|
151
|
+
"""Commit-invariant change set: tracked files differing from base (working
|
|
152
|
+
tree) plus untracked files. Sorted by repo-relative path.
|
|
153
|
+
|
|
154
|
+
Working-tree content is unchanged by a checkpoint commit, so the change set
|
|
155
|
+
and its bytes are identical before/after commit (UT-011).
|
|
156
|
+
"""
|
|
157
|
+
if not base:
|
|
158
|
+
base = _root_commit(repo_root) or _EMPTY_TREE
|
|
159
|
+
out = _git_bytes(["diff", "--name-only", "--no-renames", "-z", base], repo_root)
|
|
160
|
+
paths: set[str] = set()
|
|
161
|
+
for name in out.split(b"\x00"):
|
|
162
|
+
if name:
|
|
163
|
+
paths.add(name.decode("utf-8"))
|
|
164
|
+
out2 = _git_bytes(["ls-files", "--others", "--exclude-standard", "-z"], repo_root)
|
|
165
|
+
for name in out2.split(b"\x00"):
|
|
166
|
+
if name:
|
|
167
|
+
paths.add(name.decode("utf-8"))
|
|
168
|
+
return base, sorted(paths)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def compute_diff_hash(repo_root: Path, base: str | None = None) -> tuple[str, dict[str, Any]]:
|
|
172
|
+
"""Byte-level, commit-invariant diff hash (cluster 2).
|
|
173
|
+
|
|
174
|
+
Content-based change set: every file whose working-tree content differs
|
|
175
|
+
from base, plus untracked files. Each entry is length-framed
|
|
176
|
+
(path-len | path | exists-flag | content-len | content) so the payload is
|
|
177
|
+
independent of shell, console encoding, BOM and system newlines. Stable
|
|
178
|
+
across a checkpoint commit because git does not mutate the working tree.
|
|
179
|
+
"""
|
|
180
|
+
repo_root = Path(repo_root).resolve()
|
|
181
|
+
base, paths = _changed_paths(repo_root, base)
|
|
182
|
+
digest = hashlib.sha256()
|
|
183
|
+
digest.update(DIFF_HASH_VERSION.encode("utf-8"))
|
|
184
|
+
digest.update(b"\x00")
|
|
185
|
+
for rel in paths:
|
|
186
|
+
path_bytes = rel.encode("utf-8")
|
|
187
|
+
digest.update(len(path_bytes).to_bytes(4, "big"))
|
|
188
|
+
digest.update(path_bytes)
|
|
189
|
+
abs_path = repo_root / rel
|
|
190
|
+
if abs_path.is_file():
|
|
191
|
+
content = abs_path.read_bytes()
|
|
192
|
+
digest.update(b"\x01")
|
|
193
|
+
digest.update(len(content).to_bytes(8, "big"))
|
|
194
|
+
digest.update(content)
|
|
195
|
+
else:
|
|
196
|
+
# Deleted since base: record absence with no content.
|
|
197
|
+
digest.update(b"\x00")
|
|
198
|
+
digest.update((0).to_bytes(8, "big"))
|
|
199
|
+
try:
|
|
200
|
+
head = _git_bytes(["rev-parse", "HEAD"], repo_root).decode("utf-8", "replace").strip()
|
|
201
|
+
except RuntimeError:
|
|
202
|
+
head = ""
|
|
203
|
+
meta = {
|
|
204
|
+
"algorithmVersion": DIFF_HASH_VERSION,
|
|
205
|
+
"fileCount": len(paths),
|
|
206
|
+
"base": base,
|
|
207
|
+
"head": head or None,
|
|
208
|
+
}
|
|
209
|
+
return f"sha256:{digest.hexdigest()}", meta
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def derive_coverage(verification: str, scope: str | None) -> str:
|
|
213
|
+
"""Derive coverage lattice value from verification + scope (cluster 2)."""
|
|
214
|
+
s = str(scope).strip() if scope else ""
|
|
215
|
+
if verification == "unitTest":
|
|
216
|
+
return "module" if s in BROAD_SCOPES else "incremental"
|
|
217
|
+
if verification == "unitTestFull":
|
|
218
|
+
return "full" if s == "full" else "module"
|
|
219
|
+
if verification in ("install", "package"):
|
|
220
|
+
return "module-am"
|
|
221
|
+
return "module"
|
|
222
|
+
|
|
223
|
+
|
|
114
224
|
def expand_profile_input_files(
|
|
115
225
|
project: Path, profile_input: str
|
|
116
226
|
) -> tuple[list[str], str | None]:
|
|
@@ -275,6 +385,9 @@ def decide_can_reuse(
|
|
|
275
385
|
files: list[str],
|
|
276
386
|
requested_scope: str | None = None,
|
|
277
387
|
requested_command: str | None = None,
|
|
388
|
+
requested_toolchain_hash: str | None = None,
|
|
389
|
+
requested_profile_hash: str | None = None,
|
|
390
|
+
requested_environment_hash: str | None = None,
|
|
278
391
|
) -> dict[str, Any]:
|
|
279
392
|
ledger, ledger_path = load_ledger(change_dir)
|
|
280
393
|
if ledger is None:
|
|
@@ -282,6 +395,7 @@ def decide_can_reuse(
|
|
|
282
395
|
"ok": True,
|
|
283
396
|
"reuse": False,
|
|
284
397
|
"reason": "insufficient-evidence",
|
|
398
|
+
"code": "LEDGER_MISSING",
|
|
285
399
|
"verification": verification,
|
|
286
400
|
"detail": "ledger missing",
|
|
287
401
|
}
|
|
@@ -292,6 +406,7 @@ def decide_can_reuse(
|
|
|
292
406
|
"ok": True,
|
|
293
407
|
"reuse": False,
|
|
294
408
|
"reason": "insufficient-evidence",
|
|
409
|
+
"code": "VALIDATIONS_MISSING",
|
|
295
410
|
"verification": verification,
|
|
296
411
|
"detail": "validations missing",
|
|
297
412
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
@@ -303,6 +418,7 @@ def decide_can_reuse(
|
|
|
303
418
|
"ok": True,
|
|
304
419
|
"reuse": False,
|
|
305
420
|
"reason": "insufficient-evidence",
|
|
421
|
+
"code": "VALIDATION_MISSING",
|
|
306
422
|
"verification": verification,
|
|
307
423
|
"detail": f"validation '{verification}' missing",
|
|
308
424
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
@@ -314,6 +430,17 @@ def decide_can_reuse(
|
|
|
314
430
|
evidence = entry.get("evidence")
|
|
315
431
|
command = entry.get("command")
|
|
316
432
|
scope = entry.get("scope")
|
|
433
|
+
algorithm_version = entry.get("algorithmVersion")
|
|
434
|
+
coverage = entry.get("coverage")
|
|
435
|
+
|
|
436
|
+
# v2 fields: a v1 entry (no algorithmVersion/coverage) is conservatively
|
|
437
|
+
# invalidated once and must be re-recorded with v2 fields (COM-002). No
|
|
438
|
+
# silent upgrade of stale evidence.
|
|
439
|
+
v2_missing: list[str] = []
|
|
440
|
+
if not _nonempty_str(algorithm_version):
|
|
441
|
+
v2_missing.append("algorithmVersion")
|
|
442
|
+
if not (_nonempty_str(coverage) and str(coverage).strip() in COVERAGE_RANK):
|
|
443
|
+
v2_missing.append("coverage")
|
|
317
444
|
|
|
318
445
|
missing: list[str] = []
|
|
319
446
|
if not _nonempty_str(stored_hash):
|
|
@@ -341,22 +468,41 @@ def decide_can_reuse(
|
|
|
341
468
|
if verification == "install" and not worktree_ready(ledger, change_dir):
|
|
342
469
|
missing.append("worktree")
|
|
343
470
|
|
|
344
|
-
|
|
471
|
+
all_missing = v2_missing + missing
|
|
472
|
+
if all_missing:
|
|
345
473
|
return {
|
|
346
474
|
"ok": True,
|
|
347
475
|
"reuse": False,
|
|
348
476
|
"reason": "insufficient-evidence",
|
|
477
|
+
"code": "MISSING_V2_FIELDS" if v2_missing else "MISSING_FIELDS",
|
|
349
478
|
"verification": verification,
|
|
350
|
-
"detail": "missing or invalid: " + ", ".join(
|
|
479
|
+
"detail": "missing or invalid: " + ", ".join(all_missing),
|
|
351
480
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
352
481
|
}
|
|
353
482
|
|
|
483
|
+
# Coverage lattice: recorded coverage rank must meet the verification's
|
|
484
|
+
# required rank. Stops incremental evidence satisfying a module/full gate
|
|
485
|
+
# (UT-015 / API-005).
|
|
486
|
+
required = REQUIRED_COVERAGE.get(verification, 1)
|
|
487
|
+
if COVERAGE_RANK.get(str(coverage).strip(), -1) < required:
|
|
488
|
+
return {
|
|
489
|
+
"ok": True,
|
|
490
|
+
"reuse": False,
|
|
491
|
+
"reason": "insufficient-evidence",
|
|
492
|
+
"code": "COVERAGE_INSUFFICIENT",
|
|
493
|
+
"verification": verification,
|
|
494
|
+
"detail": f"coverage '{coverage}' below required rank {required} for {verification}",
|
|
495
|
+
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
496
|
+
"stored_coverage": coverage,
|
|
497
|
+
}
|
|
498
|
+
|
|
354
499
|
if requested_command is not None and str(requested_command).strip():
|
|
355
500
|
if str(command).strip() != str(requested_command).strip():
|
|
356
501
|
return {
|
|
357
502
|
"ok": True,
|
|
358
503
|
"reuse": False,
|
|
359
504
|
"reason": "rerun",
|
|
505
|
+
"code": "COMMAND_CHANGED",
|
|
360
506
|
"verification": verification,
|
|
361
507
|
"detail": "command changed",
|
|
362
508
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
@@ -364,11 +510,39 @@ def decide_can_reuse(
|
|
|
364
510
|
"requested_command": requested_command,
|
|
365
511
|
}
|
|
366
512
|
|
|
513
|
+
# Toolchain / profile / environment hash drift -> structured rerun (UT-017).
|
|
514
|
+
# Only compared when both the stored entry and the request carry the field.
|
|
515
|
+
for field, requested, code_name in (
|
|
516
|
+
("toolchainHash", requested_toolchain_hash, "TOOLCHAIN_CHANGED"),
|
|
517
|
+
("profileHash", requested_profile_hash, "PROFILE_CHANGED"),
|
|
518
|
+
("environmentHash", requested_environment_hash, "ENVIRONMENT_CHANGED"),
|
|
519
|
+
):
|
|
520
|
+
stored = entry.get(field)
|
|
521
|
+
if (
|
|
522
|
+
requested
|
|
523
|
+
and str(requested).strip()
|
|
524
|
+
and _nonempty_str(stored)
|
|
525
|
+
and str(stored).strip() != str(requested).strip()
|
|
526
|
+
):
|
|
527
|
+
return {
|
|
528
|
+
"ok": True,
|
|
529
|
+
"reuse": False,
|
|
530
|
+
"reason": "rerun",
|
|
531
|
+
"code": code_name,
|
|
532
|
+
"verification": verification,
|
|
533
|
+
"detail": f"{field} changed",
|
|
534
|
+
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
535
|
+
"field": field,
|
|
536
|
+
"stored": stored,
|
|
537
|
+
"requested": requested,
|
|
538
|
+
}
|
|
539
|
+
|
|
367
540
|
if verification == "unitTest" and not _scope_covers(scope, requested_scope):
|
|
368
541
|
return {
|
|
369
542
|
"ok": True,
|
|
370
543
|
"reuse": False,
|
|
371
544
|
"reason": "insufficient-evidence",
|
|
545
|
+
"code": "SCOPE_INSUFFICIENT",
|
|
372
546
|
"verification": verification,
|
|
373
547
|
"detail": "scope does not cover requested tests",
|
|
374
548
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
@@ -383,6 +557,7 @@ def decide_can_reuse(
|
|
|
383
557
|
"ok": True,
|
|
384
558
|
"reuse": False,
|
|
385
559
|
"reason": "insufficient-evidence",
|
|
560
|
+
"code": "INPUT_FILE_MISSING",
|
|
386
561
|
"verification": verification,
|
|
387
562
|
"detail": str(exc),
|
|
388
563
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
@@ -393,6 +568,7 @@ def decide_can_reuse(
|
|
|
393
568
|
"ok": True,
|
|
394
569
|
"reuse": False,
|
|
395
570
|
"reason": "rerun",
|
|
571
|
+
"code": "INPUTS_HASH_CHANGED",
|
|
396
572
|
"verification": verification,
|
|
397
573
|
"detail": "inputsHash changed",
|
|
398
574
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
@@ -405,6 +581,7 @@ def decide_can_reuse(
|
|
|
405
581
|
"ok": True,
|
|
406
582
|
"reuse": True,
|
|
407
583
|
"reason": "reuse",
|
|
584
|
+
"code": "REUSED",
|
|
408
585
|
"verification": verification,
|
|
409
586
|
"ledger_path": str(ledger_path) if ledger_path else None,
|
|
410
587
|
"inputsHash": stored_hash,
|
|
@@ -475,6 +652,9 @@ def cmd_can_reuse(args: argparse.Namespace) -> int:
|
|
|
475
652
|
files=files,
|
|
476
653
|
requested_scope=getattr(args, "scope", None),
|
|
477
654
|
requested_command=getattr(args, "command", None),
|
|
655
|
+
requested_toolchain_hash=getattr(args, "toolchain_hash", None),
|
|
656
|
+
requested_profile_hash=getattr(args, "profile_hash", None),
|
|
657
|
+
requested_environment_hash=getattr(args, "environment_hash", None),
|
|
478
658
|
)
|
|
479
659
|
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
|
480
660
|
return emit_error(f"can-reuse failed: {exc}", as_json=as_json)
|
|
@@ -542,6 +722,32 @@ def cmd_record(args: argparse.Namespace) -> int:
|
|
|
542
722
|
# would let can-reuse wrongly approve untested classes (D13 guardrail).
|
|
543
723
|
# Missing scope → can-reuse treats unitTest as insufficient-evidence.
|
|
544
724
|
|
|
725
|
+
# v2 fields (cluster 2): algorithmVersion + coverage lattice + optional
|
|
726
|
+
# toolchain/profile/environment hashes. v1 entries missing these are
|
|
727
|
+
# conservatively invalidated by can-reuse (COM-002).
|
|
728
|
+
entry["algorithmVersion"] = LEDGER_VERSION
|
|
729
|
+
coverage = getattr(args, "coverage", None)
|
|
730
|
+
if not (_nonempty_str(coverage) and str(coverage).strip() in COVERAGE_RANK):
|
|
731
|
+
coverage = derive_coverage(verification, args.scope)
|
|
732
|
+
entry["coverage"] = coverage
|
|
733
|
+
for field, attr in (
|
|
734
|
+
("toolchainHash", "toolchain_hash"),
|
|
735
|
+
("profileHash", "profile_hash"),
|
|
736
|
+
("environmentHash", "environment_hash"),
|
|
737
|
+
):
|
|
738
|
+
val = getattr(args, attr, None)
|
|
739
|
+
if _nonempty_str(val):
|
|
740
|
+
entry[field] = str(val).strip()
|
|
741
|
+
# package verification: record build artifact + test-reuse provenance.
|
|
742
|
+
if verification == "package":
|
|
743
|
+
if _nonempty_str(getattr(args, "deploy_artifact", None)):
|
|
744
|
+
entry["deployArtifact"] = str(args.deploy_artifact).strip()
|
|
745
|
+
if _nonempty_str(getattr(args, "artifact_hash", None)):
|
|
746
|
+
entry["sha256"] = str(args.artifact_hash).strip()
|
|
747
|
+
entry["testsExecuted"] = bool(getattr(args, "tests_executed", False))
|
|
748
|
+
if _nonempty_str(getattr(args, "tests_reused_from", None)):
|
|
749
|
+
entry["testsReusedFrom"] = str(args.tests_reused_from).strip()
|
|
750
|
+
|
|
545
751
|
ledger["validations"][verification] = entry
|
|
546
752
|
if "changeName" not in ledger:
|
|
547
753
|
ledger["changeName"] = change_dir.name
|
|
@@ -567,6 +773,21 @@ def cmd_record(args: argparse.Namespace) -> int:
|
|
|
567
773
|
return 0
|
|
568
774
|
|
|
569
775
|
|
|
776
|
+
def cmd_diff_hash(args: argparse.Namespace) -> int:
|
|
777
|
+
as_json = bool(args.json)
|
|
778
|
+
repo_raw = getattr(args, "repo", None)
|
|
779
|
+
repo = Path(repo_raw).expanduser().resolve() if repo_raw else Path.cwd().resolve()
|
|
780
|
+
base = getattr(args, "base", None)
|
|
781
|
+
try:
|
|
782
|
+
diff_hash, meta = compute_diff_hash(repo, base=base)
|
|
783
|
+
except (OSError, RuntimeError) as exc:
|
|
784
|
+
return emit_error(str(exc), as_json=as_json)
|
|
785
|
+
payload = {"ok": True, "action": "diff-hash", "diffHash": diff_hash}
|
|
786
|
+
payload.update(meta)
|
|
787
|
+
emit_json(payload, as_json=as_json)
|
|
788
|
+
return 0
|
|
789
|
+
|
|
790
|
+
|
|
570
791
|
def build_parser() -> argparse.ArgumentParser:
|
|
571
792
|
parser = argparse.ArgumentParser(
|
|
572
793
|
prog="harness_ledger.py",
|
|
@@ -630,6 +851,21 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
630
851
|
default=None,
|
|
631
852
|
help="optional command to compare against ledger entry",
|
|
632
853
|
)
|
|
854
|
+
p_reuse.add_argument(
|
|
855
|
+
"--toolchain-hash",
|
|
856
|
+
default=None,
|
|
857
|
+
help="optional toolchain hash to compare against ledger entry (UT-017)",
|
|
858
|
+
)
|
|
859
|
+
p_reuse.add_argument(
|
|
860
|
+
"--profile-hash",
|
|
861
|
+
default=None,
|
|
862
|
+
help="optional profile hash to compare against ledger entry (UT-017)",
|
|
863
|
+
)
|
|
864
|
+
p_reuse.add_argument(
|
|
865
|
+
"--environment-hash",
|
|
866
|
+
default=None,
|
|
867
|
+
help="optional environment hash to compare against ledger entry (UT-017)",
|
|
868
|
+
)
|
|
633
869
|
p_reuse.set_defaults(func=cmd_can_reuse)
|
|
634
870
|
|
|
635
871
|
p_record = sub.add_parser("record", parents=[shared_json], help="write validation result into ledger")
|
|
@@ -656,8 +892,38 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
656
892
|
default=None,
|
|
657
893
|
help="optional scope (default module when absent on new entries)",
|
|
658
894
|
)
|
|
895
|
+
p_record.add_argument(
|
|
896
|
+
"--coverage",
|
|
897
|
+
default=None,
|
|
898
|
+
help="optional coverage lattice value (incremental|module|module-am|full); derived when absent",
|
|
899
|
+
)
|
|
900
|
+
p_record.add_argument("--toolchain-hash", default=None)
|
|
901
|
+
p_record.add_argument("--profile-hash", default=None)
|
|
902
|
+
p_record.add_argument("--environment-hash", default=None)
|
|
903
|
+
p_record.add_argument("--deploy-artifact", default=None, help="package: built artifact path")
|
|
904
|
+
p_record.add_argument("--artifact-hash", default=None, help="package: artifact sha256")
|
|
905
|
+
p_record.add_argument(
|
|
906
|
+
"--tests-executed",
|
|
907
|
+
type=lambda v: str(v).lower() in ("1", "true", "yes", "y"),
|
|
908
|
+
default=False,
|
|
909
|
+
help="package: whether tests ran in this package lifecycle",
|
|
910
|
+
)
|
|
911
|
+
p_record.add_argument(
|
|
912
|
+
"--tests-reused-from",
|
|
913
|
+
default=None,
|
|
914
|
+
help="package: prior verifications reused when testsExecuted=false",
|
|
915
|
+
)
|
|
659
916
|
p_record.set_defaults(func=cmd_record)
|
|
660
917
|
|
|
918
|
+
p_diff = sub.add_parser(
|
|
919
|
+
"diff-hash",
|
|
920
|
+
parents=[shared_json],
|
|
921
|
+
help="compute commit-invariant byte-level diff hash for a repo",
|
|
922
|
+
)
|
|
923
|
+
p_diff.add_argument("--repo", default=None, help="repo root (default: cwd)")
|
|
924
|
+
p_diff.add_argument("--base", default=None, help="base commit (default: root commit)")
|
|
925
|
+
p_diff.set_defaults(func=cmd_diff_hash)
|
|
926
|
+
|
|
661
927
|
return parser
|
|
662
928
|
|
|
663
929
|
|