@ccoalm/ccl-skills 0.6.2 → 0.8.0
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/README.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/remind-unverified-cli-flag.sh +309 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_remind_unverified_cli_flag.sh +483 -0
- package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +5 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/SKILL.md +10 -8
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/SKILL.md +16 -17
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/client-routing.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +195 -7
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/timeout-auth-and-capabilities.md +3 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/claude_review.sh +13 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/codex_review.sh +9 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/kimi_review.sh +9 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/normalize_review_timeout.sh +22 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/opencode_review.sh +9 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +1540 -129
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_claude_review_probe.sh +8 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_compat.py +76 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +1858 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_update_review_plan_intent.sh +789 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/update_review_plan_intent.py +513 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/data-platform-architecture.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/event-driven-architecture.md +14 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/SKILL.md +5 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/SKILL.md +2 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +13 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/SKILL.md +64 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/agents/openai.yaml +4 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/async-lifecycle-and-performance.md +72 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/runtime-and-project-contract.md +58 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/source-map.md +41 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/verification-diagnostics-and-security.md +63 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +25 -9
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/source-register.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +16 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/secret-and-config-management.md +7 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/retry-timeout-circuit-breaker.md +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +8 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/design-routing-and-readiness.md +10 -14
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/verify-developer-experience.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/SKILL.md +135 -86
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/behavioral-aesthetic-logic.md +66 -80
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/delivery-contract.md +275 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-execution-checklist.md +88 -214
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-impl-naming-and-versioning.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-intake-and-acceptance.md +10 -8
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-system-source-of-truth.md +4 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/external-ui-ux-quality-benchmarks.md +112 -95
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/frontend-code-evidence-map.md +30 -21
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/interaction-design-patterns.md +22 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/layout-recipes-and-screenshot-acceptance.md +20 -17
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-project-token-consistency.md +7 -9
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-stack-strategy.md +14 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/operational-processing-workflows.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/platform-mobile-patterns.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-lifecycle-acceptance-and-iteration.md +9 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-surface-patterns.md +3 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/source-map.md +37 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/tokens-and-components.md +7 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +8 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-design-development.md +16 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/visual-craft.md +4 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/SKILL.md +5 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/architecture-playbook.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/audit-history-architecture.md +31 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/data-platform-architecture.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/event-driven-architecture.md +7 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/multi-tenant-isolation.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/notification-architecture.md +28 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/packaging-runtime-readiness.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/replay-comparison-architecture.md +28 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/workflow-state-architecture.md +39 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/SKILL.md +10 -7
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/ai-service-wiring-patterns.md +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/audit-history-patterns.md +29 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/background-job-patterns.md +16 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/batch-and-artifact-patterns.md +25 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/notification-patterns.md +40 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/public-api-security-patterns.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/replay-comparison-patterns.md +30 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +48 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/testing-and-quality-patterns.md +10 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +4 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/coverage-exhaustion-traps.md +45 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +142 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md +21 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +11 -9
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/firing-point-placement.md +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/parallel-stack-references-pattern.md +5 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/r0-leakage-audit.md +102 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +69 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +10 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/uiux-judgment-extraction.md +6 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +4 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +93 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-parallel-stack-parity.sh +119 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +22 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/impact-chain-gate.rb +49 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/obligation-ledger.py +2748 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/register-firing-path-resolution.rb +20 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/shared_git_surface_gate.py +1142 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_parallel_stack_parity.sh +183 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +19 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh +41 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ci_checkout_ref_binding.sh +120 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_entrypoint_domain_scan_terms.sh +82 -8
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +336 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_self_adjudication.sh +82 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger.sh +1416 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger_repo_audit.sh +57 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_wiring.sh +141 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_routing_pointer_integrity.sh +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_shared_git_surface_gate.sh +1696 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_delivery_contract.sh +2117 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_loading_budget.sh +316 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +1176 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_skill_cross_refs.sh +31 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate-skill.sh +9 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +980 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/terminal-cli-dev/SKILL.md +9 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +11 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/client-runtime-test-matrices.md +10 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/fitness-functions.md +16 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/scenario-testing.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +16 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +5 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/delivery-face-closeout.md +16 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/self-benchmark-baseline.md +37 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +7 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/complex-workspace-patterns.md +1 -1
- package/dist/assets/release.json +275 -105
- package/package.json +1 -1
|
@@ -0,0 +1,1176 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regression for receipt-bound extraction review terminal-state validation.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
|
6
|
+
VALIDATOR="$SCRIPT_DIR/validate_extraction_review_state.py"
|
|
7
|
+
[ -f "$VALIDATOR" ] || { echo "FAIL: validator not found: $VALIDATOR" >&2; exit 1; }
|
|
8
|
+
|
|
9
|
+
TMP="$(mktemp -d "${TMPDIR:-/tmp}/extraction-review-state.XXXXXX")"
|
|
10
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
11
|
+
|
|
12
|
+
python3 - "$VALIDATOR" "$TMP" <<'PY'
|
|
13
|
+
import copy
|
|
14
|
+
import hashlib
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
validator = Path(sys.argv[1])
|
|
22
|
+
root = Path(sys.argv[2])
|
|
23
|
+
A, B, C = (character * 40 for character in "abc")
|
|
24
|
+
CANDIDATE = "d" * 64
|
|
25
|
+
OTHER_CANDIDATE = "e" * 64
|
|
26
|
+
CLOSED_DISPOSITIONS = {
|
|
27
|
+
"fixed",
|
|
28
|
+
"source_refuted",
|
|
29
|
+
"accepted_tradeoff",
|
|
30
|
+
"pre_existing_out_of_scope",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def canonical_hash(value):
|
|
35
|
+
encoded = json.dumps(
|
|
36
|
+
value, ensure_ascii=False, sort_keys=True, separators=(",", ":")
|
|
37
|
+
).encode()
|
|
38
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def write_json(name, payload):
|
|
42
|
+
encoded = (json.dumps(payload, ensure_ascii=False, indent=2) + "\n").encode()
|
|
43
|
+
(root / name).write_bytes(encoded)
|
|
44
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def encode_duplicate_key(payload, field, first_value):
|
|
48
|
+
pairs = []
|
|
49
|
+
for key, value in payload.items():
|
|
50
|
+
if key == field:
|
|
51
|
+
pairs.append((key, first_value))
|
|
52
|
+
pairs.append((key, value))
|
|
53
|
+
return (
|
|
54
|
+
"{"
|
|
55
|
+
+ ",".join(
|
|
56
|
+
f"{json.dumps(key)}:{json.dumps(value, ensure_ascii=False)}"
|
|
57
|
+
for key, value in pairs
|
|
58
|
+
)
|
|
59
|
+
+ "}\n"
|
|
60
|
+
).encode()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def write_duplicate_json(name, payload, field, first_value):
|
|
64
|
+
encoded = encode_duplicate_key(payload, field, first_value)
|
|
65
|
+
(root / name).write_bytes(encoded)
|
|
66
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def write_base_evidence(name, base_sha, ref="refs/heads/dev"):
|
|
70
|
+
encoded = f"{base_sha}\t{ref}\n".encode()
|
|
71
|
+
(root / name).write_bytes(encoded)
|
|
72
|
+
return hashlib.sha256(encoded).hexdigest()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def occurrence_ref(occurrence):
|
|
76
|
+
return {
|
|
77
|
+
"receipt_sha256": occurrence["receipt_sha256"],
|
|
78
|
+
"finding_sha256": occurrence["finding_sha256"],
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def bind_disposition_evidence(name, occurrence, *, resolves=None, evidence=None):
|
|
83
|
+
if resolves is None:
|
|
84
|
+
resolves = [occurrence_ref(occurrence)]
|
|
85
|
+
if evidence is None:
|
|
86
|
+
evidence = [
|
|
87
|
+
f"synthetic {occurrence['disposition']} evidence for the current candidate"
|
|
88
|
+
]
|
|
89
|
+
payload = {
|
|
90
|
+
"schema_version": 1,
|
|
91
|
+
"candidate_sha256": CANDIDATE,
|
|
92
|
+
"receipt_sha256": occurrence["receipt_sha256"],
|
|
93
|
+
"finding_sha256": occurrence["finding_sha256"],
|
|
94
|
+
"disposition": occurrence["disposition"],
|
|
95
|
+
"resolves_occurrences": copy.deepcopy(resolves),
|
|
96
|
+
"evidence": list(evidence),
|
|
97
|
+
}
|
|
98
|
+
evidence_hash = write_json(name, payload)
|
|
99
|
+
occurrence.update(
|
|
100
|
+
disposition_evidence_file=name,
|
|
101
|
+
disposition_evidence_sha256=evidence_hash,
|
|
102
|
+
)
|
|
103
|
+
return payload
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def clear_disposition_evidence(occurrence):
|
|
107
|
+
occurrence.pop("disposition_evidence_file", None)
|
|
108
|
+
occurrence.pop("disposition_evidence_sha256", None)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def mutate_disposition_evidence(occurrence, mutator):
|
|
112
|
+
evidence_path = root / occurrence["disposition_evidence_file"]
|
|
113
|
+
payload = json.loads(evidence_path.read_text(encoding="utf-8"))
|
|
114
|
+
mutator(payload)
|
|
115
|
+
occurrence["disposition_evidence_sha256"] = write_json(
|
|
116
|
+
occurrence["disposition_evidence_file"], payload
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def duplicate_disposition_evidence_key(occurrence, field, first_value):
|
|
121
|
+
evidence_path = root / occurrence["disposition_evidence_file"]
|
|
122
|
+
payload = json.loads(evidence_path.read_text(encoding="utf-8"))
|
|
123
|
+
encoded = encode_duplicate_key(payload, field, first_value)
|
|
124
|
+
evidence_path.write_bytes(encoded)
|
|
125
|
+
occurrence["disposition_evidence_sha256"] = hashlib.sha256(encoded).hexdigest()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def finding(number):
|
|
129
|
+
return {
|
|
130
|
+
"severity": "P1",
|
|
131
|
+
"title": f"receipt-bound finding {number}",
|
|
132
|
+
"body": f"failure path {number}",
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def make_fixture(
|
|
137
|
+
name,
|
|
138
|
+
*,
|
|
139
|
+
receipt_findings=None,
|
|
140
|
+
receipt_mutators=None,
|
|
141
|
+
completion=True,
|
|
142
|
+
completion_mutator=None,
|
|
143
|
+
budget=2,
|
|
144
|
+
base_shas=None,
|
|
145
|
+
closeout=None,
|
|
146
|
+
unmatched=0,
|
|
147
|
+
open_last=False,
|
|
148
|
+
):
|
|
149
|
+
if receipt_findings is None:
|
|
150
|
+
receipt_findings = [[finding(1)], []]
|
|
151
|
+
receipt_mutators = receipt_mutators or {}
|
|
152
|
+
rounds = len(receipt_findings)
|
|
153
|
+
scope = {
|
|
154
|
+
"schema_version": 3,
|
|
155
|
+
"intent_sha256": "1" * 64,
|
|
156
|
+
"acceptance_sha256": "2" * 64,
|
|
157
|
+
"stage": "release",
|
|
158
|
+
"review_depth": "release",
|
|
159
|
+
"risk_tags": ["shared-gate"],
|
|
160
|
+
"challenge_budget": budget,
|
|
161
|
+
"wording_only_proof_sha256": None,
|
|
162
|
+
"wording_only_scope_sha256": None,
|
|
163
|
+
}
|
|
164
|
+
scope_sha = canonical_hash(scope)
|
|
165
|
+
receipts = []
|
|
166
|
+
receipt_hashes = []
|
|
167
|
+
receipt_refs = []
|
|
168
|
+
for index, findings in enumerate(receipt_findings, start=1):
|
|
169
|
+
mode = "review" if index == 1 else "challenge"
|
|
170
|
+
status = "findings" if findings else "passed"
|
|
171
|
+
remaining = 3 - index
|
|
172
|
+
if status == "findings":
|
|
173
|
+
state = "post_review_budget" if index == 3 else "findings_pending"
|
|
174
|
+
else:
|
|
175
|
+
state = "reviewed"
|
|
176
|
+
receipt = {
|
|
177
|
+
"schema_version": 3,
|
|
178
|
+
"mode": mode,
|
|
179
|
+
"status": status,
|
|
180
|
+
"findings": copy.deepcopy(findings),
|
|
181
|
+
"review_chain_tracked": True,
|
|
182
|
+
"review_chain_id": "extraction-chain",
|
|
183
|
+
"autonomous_review_index": index,
|
|
184
|
+
"autonomous_review_budget": 3,
|
|
185
|
+
"autonomous_reviews_remaining": remaining,
|
|
186
|
+
"autonomous_review_allowed": remaining > 0,
|
|
187
|
+
"challenge_index": 0 if index == 1 else index - 1,
|
|
188
|
+
"challenge_budget": budget,
|
|
189
|
+
"prior_review_result_sha256": list(receipt_hashes),
|
|
190
|
+
"candidate_sha256": CANDIDATE,
|
|
191
|
+
"packet_sha256": CANDIDATE,
|
|
192
|
+
"review_scope": copy.deepcopy(scope),
|
|
193
|
+
"review_scope_sha256": scope_sha,
|
|
194
|
+
"wording_only_proof_sha256": None,
|
|
195
|
+
"wording_only_scope": None,
|
|
196
|
+
"stage": "release",
|
|
197
|
+
"review_depth": "release",
|
|
198
|
+
"risk_tags": ["shared-gate"],
|
|
199
|
+
"review_state": state,
|
|
200
|
+
"human_decision_required": state == "post_review_budget",
|
|
201
|
+
}
|
|
202
|
+
mutator = receipt_mutators.get(index)
|
|
203
|
+
if mutator is not None:
|
|
204
|
+
mutator(receipt)
|
|
205
|
+
receipt_name = f"{name}-round-{index}.json"
|
|
206
|
+
receipt_hash = write_json(receipt_name, receipt)
|
|
207
|
+
receipts.append(receipt)
|
|
208
|
+
receipt_hashes.append(receipt_hash)
|
|
209
|
+
receipt_refs.append(
|
|
210
|
+
{"sequence": index, "file": receipt_name, "sha256": receipt_hash}
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
completion_ref = None
|
|
214
|
+
if completion:
|
|
215
|
+
final = receipts[-1]
|
|
216
|
+
complete = {
|
|
217
|
+
"schema_version": 3,
|
|
218
|
+
"mode": "complete",
|
|
219
|
+
"status": "passed",
|
|
220
|
+
"findings": [],
|
|
221
|
+
"review_chain_tracked": True,
|
|
222
|
+
"review_chain_id": final["review_chain_id"],
|
|
223
|
+
"autonomous_review_index": final["autonomous_review_index"],
|
|
224
|
+
"autonomous_review_budget": 3,
|
|
225
|
+
"autonomous_reviews_remaining": final["autonomous_reviews_remaining"],
|
|
226
|
+
"autonomous_review_allowed": False,
|
|
227
|
+
"challenge_budget": budget,
|
|
228
|
+
"prior_review_result_sha256": copy.deepcopy(
|
|
229
|
+
final["prior_review_result_sha256"]
|
|
230
|
+
),
|
|
231
|
+
"candidate_sha256": final["candidate_sha256"],
|
|
232
|
+
"packet_sha256": final["packet_sha256"],
|
|
233
|
+
"review_scope": copy.deepcopy(final["review_scope"]),
|
|
234
|
+
"review_scope_sha256": final["review_scope_sha256"],
|
|
235
|
+
"wording_only_proof_sha256": None,
|
|
236
|
+
"wording_only_scope": None,
|
|
237
|
+
"stage": final["stage"],
|
|
238
|
+
"review_depth": final["review_depth"],
|
|
239
|
+
"risk_tags": copy.deepcopy(final["risk_tags"]),
|
|
240
|
+
"review_state": "self_reviewed",
|
|
241
|
+
"completion_gated": False,
|
|
242
|
+
"completion_review_result_sha256": receipt_hashes[-1],
|
|
243
|
+
"next_action": "complete",
|
|
244
|
+
}
|
|
245
|
+
if completion_mutator is not None:
|
|
246
|
+
completion_mutator(complete)
|
|
247
|
+
complete_name = f"{name}-complete.json"
|
|
248
|
+
complete_hash = write_json(complete_name, complete)
|
|
249
|
+
completion_ref = {"file": complete_name, "sha256": complete_hash}
|
|
250
|
+
|
|
251
|
+
if base_shas is None:
|
|
252
|
+
base_shas = [A] * rounds
|
|
253
|
+
attestations = []
|
|
254
|
+
for sequence, base_sha in enumerate(base_shas, start=1):
|
|
255
|
+
evidence_name = f"{name}-base-{sequence}.txt"
|
|
256
|
+
evidence_hash = write_base_evidence(evidence_name, base_sha)
|
|
257
|
+
attestations.append(
|
|
258
|
+
{
|
|
259
|
+
"sequence": sequence,
|
|
260
|
+
"remote": "origin",
|
|
261
|
+
"ref": "refs/heads/dev",
|
|
262
|
+
"sha": base_sha,
|
|
263
|
+
"confirmed_at": f"2026-08-29T12:{sequence:02d}:00Z",
|
|
264
|
+
"controller_receipt_sha256": (
|
|
265
|
+
receipt_hashes[sequence - 1] if sequence <= rounds else None
|
|
266
|
+
),
|
|
267
|
+
"evidence_file": evidence_name,
|
|
268
|
+
"evidence_sha256": evidence_hash,
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
occurrences = []
|
|
273
|
+
for receipt, receipt_hash in zip(receipts, receipt_hashes):
|
|
274
|
+
for item in receipt["findings"]:
|
|
275
|
+
occurrences.append(
|
|
276
|
+
{
|
|
277
|
+
"receipt_sha256": receipt_hash,
|
|
278
|
+
"finding_sha256": canonical_hash(item),
|
|
279
|
+
"disposition": "fixed",
|
|
280
|
+
}
|
|
281
|
+
)
|
|
282
|
+
if open_last and occurrences:
|
|
283
|
+
occurrences[-1]["disposition"] = "open"
|
|
284
|
+
for occurrence_index, occurrence in enumerate(occurrences, start=1):
|
|
285
|
+
if occurrence["disposition"] in CLOSED_DISPOSITIONS:
|
|
286
|
+
bind_disposition_evidence(
|
|
287
|
+
f"{name}-disposition-{occurrence_index}.json",
|
|
288
|
+
occurrence,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
classes = []
|
|
292
|
+
if occurrences:
|
|
293
|
+
sweep = None
|
|
294
|
+
if len(occurrences) >= 3:
|
|
295
|
+
searched_set = [
|
|
296
|
+
"skills/code-review",
|
|
297
|
+
"skills/skill-extraction-workflow",
|
|
298
|
+
]
|
|
299
|
+
unmatched_items = [f"unmatched-{index}" for index in range(unmatched)]
|
|
300
|
+
manifest = {
|
|
301
|
+
"schema_version": 1,
|
|
302
|
+
"candidate_sha256": CANDIDATE,
|
|
303
|
+
"searched_set": searched_set,
|
|
304
|
+
"unmatched_instances": unmatched_items,
|
|
305
|
+
}
|
|
306
|
+
manifest_name = f"{name}-sweep.json"
|
|
307
|
+
manifest_hash = write_json(manifest_name, manifest)
|
|
308
|
+
sweep = {
|
|
309
|
+
"candidate_sha256": CANDIDATE,
|
|
310
|
+
"manifest_file": manifest_name,
|
|
311
|
+
"manifest_sha256": manifest_hash,
|
|
312
|
+
"searched_set": searched_set,
|
|
313
|
+
"unmatched_instances": unmatched,
|
|
314
|
+
}
|
|
315
|
+
classes.append(
|
|
316
|
+
{
|
|
317
|
+
"key": "receipt-binding",
|
|
318
|
+
"root_cause_predicate": "A closeout field is accepted without controller evidence.",
|
|
319
|
+
"affected_surface": "extraction closeout ledger",
|
|
320
|
+
"occurrences": occurrences,
|
|
321
|
+
"authoritative_sweep": sweep,
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if closeout is None:
|
|
326
|
+
closeout = (
|
|
327
|
+
"ready_for_human_decision"
|
|
328
|
+
if completion
|
|
329
|
+
else "continuation_authorization_required"
|
|
330
|
+
)
|
|
331
|
+
controller_state = (
|
|
332
|
+
"self_reviewed" if completion else receipts[-1]["review_state"]
|
|
333
|
+
)
|
|
334
|
+
ledger = {
|
|
335
|
+
"schema_version": 3,
|
|
336
|
+
"candidate_sha256": CANDIDATE,
|
|
337
|
+
"controller_receipts": receipt_refs,
|
|
338
|
+
"completion_receipt": completion_ref,
|
|
339
|
+
"base_attestations": attestations,
|
|
340
|
+
"autonomous_round": rounds,
|
|
341
|
+
"controller_review_state": controller_state,
|
|
342
|
+
"finding_classes": classes,
|
|
343
|
+
"unreviewed_delta": [],
|
|
344
|
+
"closeout_state": closeout,
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
"ledger": ledger,
|
|
348
|
+
"receipts": receipts,
|
|
349
|
+
"receipt_hashes": receipt_hashes,
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def invoke_encoded(name, encoded):
|
|
354
|
+
path = root / f"{name}-ledger.json"
|
|
355
|
+
path.write_bytes(encoded)
|
|
356
|
+
try:
|
|
357
|
+
return subprocess.run(
|
|
358
|
+
[sys.executable, str(validator), str(path)],
|
|
359
|
+
stdout=subprocess.PIPE,
|
|
360
|
+
stderr=subprocess.STDOUT,
|
|
361
|
+
text=True,
|
|
362
|
+
check=False,
|
|
363
|
+
timeout=5,
|
|
364
|
+
)
|
|
365
|
+
except subprocess.TimeoutExpired as exc:
|
|
366
|
+
raise AssertionError((name, "validator blocked", exc)) from exc
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def invoke(name, payload):
|
|
370
|
+
encoded = (json.dumps(payload, indent=2) + "\n").encode()
|
|
371
|
+
return invoke_encoded(name, encoded)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def run(name, payload, expected_rc, token):
|
|
375
|
+
result = invoke(name, payload)
|
|
376
|
+
assert result.returncode == expected_rc, (name, result.returncode, result.stdout)
|
|
377
|
+
assert token in result.stdout, (name, token, result.stdout)
|
|
378
|
+
assert "Traceback" not in result.stdout, (name, result.stdout)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
ready = make_fixture("ready")
|
|
382
|
+
run("ready", ready["ledger"], 0, "closeout_state=ready_for_human_decision")
|
|
383
|
+
|
|
384
|
+
schema_v3 = make_fixture("schema-v3")
|
|
385
|
+
schema_v3["ledger"]["schema_version"] = 3
|
|
386
|
+
legacy_v2 = make_fixture("legacy-v2")
|
|
387
|
+
legacy_v2["ledger"]["schema_version"] = 2
|
|
388
|
+
schema_results = [
|
|
389
|
+
("schema-v3", invoke("schema-v3", schema_v3["ledger"])),
|
|
390
|
+
("legacy-v2", invoke("legacy-v2", legacy_v2["ledger"])),
|
|
391
|
+
]
|
|
392
|
+
assert [
|
|
393
|
+
(result.returncode, expected_token in result.stdout, "Traceback" in result.stdout)
|
|
394
|
+
for (name, result), expected_token in zip(
|
|
395
|
+
schema_results,
|
|
396
|
+
("closeout_state=ready_for_human_decision", "schema_version must be 3"),
|
|
397
|
+
strict=True,
|
|
398
|
+
)
|
|
399
|
+
] == [(0, True, False), (1, True, False)], [
|
|
400
|
+
(name, result.returncode, result.stdout) for name, result in schema_results
|
|
401
|
+
]
|
|
402
|
+
|
|
403
|
+
bad_digest = make_fixture("bad-digest")
|
|
404
|
+
bad_digest["ledger"]["controller_receipts"][0]["sha256"] = "f" * 64
|
|
405
|
+
run("bad-digest", bad_digest["ledger"], 1, "digest does not match")
|
|
406
|
+
|
|
407
|
+
# JSON floats equal to the expected integers must not satisfy integer guards.
|
|
408
|
+
float_schema = make_fixture("float-schema")
|
|
409
|
+
float_schema["ledger"]["schema_version"] = 3.0
|
|
410
|
+
run("float-schema", float_schema["ledger"], 1, "schema_version must be 3")
|
|
411
|
+
|
|
412
|
+
float_budget = make_fixture(
|
|
413
|
+
"float-budget",
|
|
414
|
+
receipt_mutators={2: lambda row: row.update(challenge_budget=2.0)},
|
|
415
|
+
)
|
|
416
|
+
run("float-budget", float_budget["ledger"], 1, "challenge_budget must be 2")
|
|
417
|
+
|
|
418
|
+
nan_schema = make_fixture("nan-schema")
|
|
419
|
+
nan_schema["ledger"]["schema_version"] = float("nan")
|
|
420
|
+
run("nan-schema", nan_schema["ledger"], 1, "non-standard JSON constant")
|
|
421
|
+
|
|
422
|
+
control_chain = make_fixture(
|
|
423
|
+
"control-chain",
|
|
424
|
+
receipt_mutators={
|
|
425
|
+
2: lambda row: row.update(review_chain_id="chain\x1b[31mred")
|
|
426
|
+
},
|
|
427
|
+
)
|
|
428
|
+
run("control-chain", control_chain["ledger"], 1, "must not contain control characters")
|
|
429
|
+
|
|
430
|
+
# C1 controls and Unicode line/paragraph separators forge diagnostic line
|
|
431
|
+
# breaks just like C0 escapes; each must be rejected, not echoed.
|
|
432
|
+
for label, hostile in (
|
|
433
|
+
("c1-csi", "chain\x9bred"),
|
|
434
|
+
("c1-nel", "chain\x85red"),
|
|
435
|
+
("line-sep", "chain\u2028red"),
|
|
436
|
+
("para-sep", "chain\u2029red"),
|
|
437
|
+
):
|
|
438
|
+
hostile_chain = make_fixture(
|
|
439
|
+
f"hostile-{label}",
|
|
440
|
+
receipt_mutators={2: lambda row, value=hostile: row.update(review_chain_id=value)},
|
|
441
|
+
)
|
|
442
|
+
run(
|
|
443
|
+
f"hostile-{label}",
|
|
444
|
+
hostile_chain["ledger"],
|
|
445
|
+
1,
|
|
446
|
+
"must not contain control characters",
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# A float completion schema_version must not satisfy the completion guard.
|
|
450
|
+
float_completion = make_fixture(
|
|
451
|
+
"float-completion",
|
|
452
|
+
completion_mutator=lambda row: row.update(schema_version=3.0),
|
|
453
|
+
)
|
|
454
|
+
run(
|
|
455
|
+
"float-completion",
|
|
456
|
+
float_completion["ledger"],
|
|
457
|
+
1,
|
|
458
|
+
"completion receipt must be a passed self_reviewed complete result",
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
wrong_chain = make_fixture(
|
|
462
|
+
"wrong-chain",
|
|
463
|
+
receipt_mutators={2: lambda row: row.update(review_chain_id="other-chain")},
|
|
464
|
+
)
|
|
465
|
+
run("wrong-chain", wrong_chain["ledger"], 1, "review_chain_id")
|
|
466
|
+
|
|
467
|
+
wrong_mode = make_fixture(
|
|
468
|
+
"wrong-mode", receipt_mutators={2: lambda row: row.update(mode="review")}
|
|
469
|
+
)
|
|
470
|
+
run("wrong-mode", wrong_mode["ledger"], 1, "mode challenge")
|
|
471
|
+
|
|
472
|
+
wrong_prior = make_fixture(
|
|
473
|
+
"wrong-prior",
|
|
474
|
+
receipt_mutators={
|
|
475
|
+
2: lambda row: row.update(prior_review_result_sha256=["f" * 64])
|
|
476
|
+
},
|
|
477
|
+
)
|
|
478
|
+
run("wrong-prior", wrong_prior["ledger"], 1, "prior_review_result_sha256")
|
|
479
|
+
|
|
480
|
+
packet_mismatch = make_fixture(
|
|
481
|
+
"packet-mismatch",
|
|
482
|
+
receipt_mutators={2: lambda row: row.update(packet_sha256=OTHER_CANDIDATE)},
|
|
483
|
+
)
|
|
484
|
+
run("packet-mismatch", packet_mismatch["ledger"], 1, "packet_sha256 must equal candidate_sha256")
|
|
485
|
+
|
|
486
|
+
def drift_scope(row):
|
|
487
|
+
row["review_scope"]["stage"] = "build"
|
|
488
|
+
row["review_scope_sha256"] = canonical_hash(row["review_scope"])
|
|
489
|
+
row["stage"] = "build"
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
scope_mismatch = make_fixture("scope-mismatch", receipt_mutators={2: drift_scope})
|
|
493
|
+
run("scope-mismatch", scope_mismatch["ledger"], 1, "review scope changed")
|
|
494
|
+
|
|
495
|
+
budget_four = make_fixture("budget-four", budget=4)
|
|
496
|
+
run("budget-four", budget_four["ledger"], 1, "challenge_budget must be 2")
|
|
497
|
+
|
|
498
|
+
review_only = make_fixture("review-only", receipt_findings=[[]])
|
|
499
|
+
run("review-only", review_only["ledger"], 1, "ready requires at least one tracked challenge")
|
|
500
|
+
|
|
501
|
+
missing_complete = make_fixture("missing-complete")
|
|
502
|
+
missing_complete["ledger"]["completion_receipt"] = None
|
|
503
|
+
run("missing-complete", missing_complete["ledger"], 1, "requires a completion receipt")
|
|
504
|
+
|
|
505
|
+
bad_complete_state = make_fixture(
|
|
506
|
+
"bad-complete-state",
|
|
507
|
+
completion_mutator=lambda row: row.update(review_state="reviewed"),
|
|
508
|
+
)
|
|
509
|
+
run("bad-complete-state", bad_complete_state["ledger"], 1, "passed self_reviewed")
|
|
510
|
+
|
|
511
|
+
bad_complete_hash = make_fixture(
|
|
512
|
+
"bad-complete-hash",
|
|
513
|
+
completion_mutator=lambda row: row.update(
|
|
514
|
+
completion_review_result_sha256="f" * 64
|
|
515
|
+
),
|
|
516
|
+
)
|
|
517
|
+
run("bad-complete-hash", bad_complete_hash["ledger"], 1, "completion_review_result_sha256")
|
|
518
|
+
|
|
519
|
+
bad_complete_candidate = make_fixture(
|
|
520
|
+
"bad-complete-candidate",
|
|
521
|
+
completion_mutator=lambda row: row.update(
|
|
522
|
+
candidate_sha256=OTHER_CANDIDATE, packet_sha256=OTHER_CANDIDATE
|
|
523
|
+
),
|
|
524
|
+
)
|
|
525
|
+
run("bad-complete-candidate", bad_complete_candidate["ledger"], 1, "exact final candidate")
|
|
526
|
+
|
|
527
|
+
bad_complete_remaining = make_fixture(
|
|
528
|
+
"bad-complete-remaining",
|
|
529
|
+
completion_mutator=lambda row: row.update(autonomous_reviews_remaining=2),
|
|
530
|
+
)
|
|
531
|
+
run(
|
|
532
|
+
"bad-complete-remaining",
|
|
533
|
+
bad_complete_remaining["ledger"],
|
|
534
|
+
1,
|
|
535
|
+
"autonomous_reviews_remaining",
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
continuation = make_fixture(
|
|
539
|
+
"continuation",
|
|
540
|
+
receipt_findings=[[finding(1)], [], [finding(2)]],
|
|
541
|
+
completion=False,
|
|
542
|
+
open_last=True,
|
|
543
|
+
)
|
|
544
|
+
run(
|
|
545
|
+
"continuation",
|
|
546
|
+
continuation["ledger"],
|
|
547
|
+
0,
|
|
548
|
+
"closeout_state=continuation_authorization_required",
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
continuation_unconsumed_first_drift = make_fixture(
|
|
552
|
+
"continuation-unconsumed-first-drift",
|
|
553
|
+
receipt_findings=[[finding(1)], [], [finding(2)]],
|
|
554
|
+
completion=False,
|
|
555
|
+
open_last=True,
|
|
556
|
+
base_shas=[A, A, A, B],
|
|
557
|
+
)
|
|
558
|
+
run(
|
|
559
|
+
"continuation-unconsumed-first-drift",
|
|
560
|
+
continuation_unconsumed_first_drift["ledger"],
|
|
561
|
+
1,
|
|
562
|
+
"final controller receipt does not consume the latest attested base",
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
continuation_candidate = make_fixture(
|
|
566
|
+
"continuation-candidate",
|
|
567
|
+
receipt_findings=[[finding(1)], [], [finding(2)]],
|
|
568
|
+
completion=False,
|
|
569
|
+
open_last=True,
|
|
570
|
+
)
|
|
571
|
+
continuation_candidate["ledger"]["candidate_sha256"] = OTHER_CANDIDATE
|
|
572
|
+
run(
|
|
573
|
+
"continuation-candidate",
|
|
574
|
+
continuation_candidate["ledger"],
|
|
575
|
+
1,
|
|
576
|
+
"controller receipt 1 does not bind the ledger candidate",
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# A chain whose FIRST round reviewed a different candidate must not close out
|
|
580
|
+
# a ledger for the final candidate, even when the final round binds it.
|
|
581
|
+
drifted_round = make_fixture(
|
|
582
|
+
"drifted-round-candidate",
|
|
583
|
+
receipt_mutators={
|
|
584
|
+
1: lambda row: row.update(
|
|
585
|
+
candidate_sha256=OTHER_CANDIDATE, packet_sha256=OTHER_CANDIDATE
|
|
586
|
+
)
|
|
587
|
+
},
|
|
588
|
+
)
|
|
589
|
+
run(
|
|
590
|
+
"drifted-round-candidate",
|
|
591
|
+
drifted_round["ledger"],
|
|
592
|
+
1,
|
|
593
|
+
"controller receipt 1 does not bind the ledger candidate",
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
unknown_state = make_fixture(
|
|
597
|
+
"unknown-state",
|
|
598
|
+
receipt_findings=[[finding(1)], [], [finding(2)]],
|
|
599
|
+
receipt_mutators={3: lambda row: row.update(review_state="potato")},
|
|
600
|
+
completion=False,
|
|
601
|
+
open_last=True,
|
|
602
|
+
)
|
|
603
|
+
unknown_state["ledger"]["controller_review_state"] = "potato"
|
|
604
|
+
run("unknown-state", unknown_state["ledger"], 1, "unknown controller review_state")
|
|
605
|
+
|
|
606
|
+
passed_continuation = make_fixture(
|
|
607
|
+
"passed-continuation",
|
|
608
|
+
receipt_findings=[[finding(1)], [], []],
|
|
609
|
+
completion=False,
|
|
610
|
+
)
|
|
611
|
+
run("passed-continuation", passed_continuation["ledger"], 1, "round 3 findings in post_review_budget")
|
|
612
|
+
|
|
613
|
+
bad_finding_hash = make_fixture("bad-finding-hash")
|
|
614
|
+
bad_finding_hash["ledger"]["finding_classes"][0]["occurrences"][0][
|
|
615
|
+
"finding_sha256"
|
|
616
|
+
] = "f" * 64
|
|
617
|
+
run("bad-finding-hash", bad_finding_hash["ledger"], 1, "does not identify a finding")
|
|
618
|
+
|
|
619
|
+
omitted_finding = make_fixture("omitted-finding")
|
|
620
|
+
omitted_finding["ledger"]["finding_classes"] = []
|
|
621
|
+
run("omitted-finding", omitted_finding["ledger"], 1, "omits controller findings")
|
|
622
|
+
|
|
623
|
+
reordered_findings = make_fixture(
|
|
624
|
+
"reordered-findings", receipt_findings=[[finding(1), finding(2)], []]
|
|
625
|
+
)
|
|
626
|
+
reordered_findings["ledger"]["finding_classes"][0]["occurrences"].reverse()
|
|
627
|
+
run("reordered-findings", reordered_findings["ledger"], 1, "not in controller receipt order")
|
|
628
|
+
|
|
629
|
+
fixed_without_evidence = make_fixture("fixed-without-evidence")
|
|
630
|
+
clear_disposition_evidence(
|
|
631
|
+
fixed_without_evidence["ledger"]["finding_classes"][0]["occurrences"][-1]
|
|
632
|
+
)
|
|
633
|
+
run(
|
|
634
|
+
"fixed-without-evidence",
|
|
635
|
+
fixed_without_evidence["ledger"],
|
|
636
|
+
1,
|
|
637
|
+
"disposition_evidence_file",
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
for disposition in ("accepted_tradeoff", "pre_existing_out_of_scope"):
|
|
641
|
+
case_name = f"{disposition.replace('_', '-')}-without-evidence"
|
|
642
|
+
without_evidence = make_fixture(case_name)
|
|
643
|
+
without_evidence_occurrence = without_evidence["ledger"]["finding_classes"][0][
|
|
644
|
+
"occurrences"
|
|
645
|
+
][-1]
|
|
646
|
+
without_evidence_occurrence["disposition"] = disposition
|
|
647
|
+
clear_disposition_evidence(without_evidence_occurrence)
|
|
648
|
+
run(case_name, without_evidence["ledger"], 1, "disposition_evidence_file")
|
|
649
|
+
|
|
650
|
+
bound_case_name = f"{disposition.replace('_', '-')}-bound"
|
|
651
|
+
bound = make_fixture(bound_case_name)
|
|
652
|
+
bound_occurrence = bound["ledger"]["finding_classes"][0]["occurrences"][-1]
|
|
653
|
+
bound_occurrence["disposition"] = disposition
|
|
654
|
+
bind_disposition_evidence(
|
|
655
|
+
f"{bound_case_name}-disposition.json",
|
|
656
|
+
bound_occurrence,
|
|
657
|
+
)
|
|
658
|
+
run(bound_case_name, bound["ledger"], 0, "ready_for_human_decision")
|
|
659
|
+
|
|
660
|
+
stale_disposition_evidence = make_fixture("stale-disposition-evidence")
|
|
661
|
+
stale_disposition_evidence_occurrence = stale_disposition_evidence["ledger"][
|
|
662
|
+
"finding_classes"
|
|
663
|
+
][0]["occurrences"][-1]
|
|
664
|
+
mutate_disposition_evidence(
|
|
665
|
+
stale_disposition_evidence_occurrence,
|
|
666
|
+
lambda evidence: evidence.update(candidate_sha256=OTHER_CANDIDATE),
|
|
667
|
+
)
|
|
668
|
+
run(
|
|
669
|
+
"stale-disposition-evidence",
|
|
670
|
+
stale_disposition_evidence["ledger"],
|
|
671
|
+
1,
|
|
672
|
+
"stale for the current candidate",
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
wrong_disposition_evidence = make_fixture("wrong-disposition-evidence")
|
|
676
|
+
wrong_disposition_evidence_occurrence = wrong_disposition_evidence["ledger"][
|
|
677
|
+
"finding_classes"
|
|
678
|
+
][0]["occurrences"][-1]
|
|
679
|
+
mutate_disposition_evidence(
|
|
680
|
+
wrong_disposition_evidence_occurrence,
|
|
681
|
+
lambda evidence: evidence.update(disposition="accepted_tradeoff"),
|
|
682
|
+
)
|
|
683
|
+
run(
|
|
684
|
+
"wrong-disposition-evidence",
|
|
685
|
+
wrong_disposition_evidence["ledger"],
|
|
686
|
+
1,
|
|
687
|
+
"does not bind its disposition",
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
duplicate_key_cases = []
|
|
691
|
+
for case_name, field, first_value in (
|
|
692
|
+
("duplicate-candidate-evidence", "candidate_sha256", OTHER_CANDIDATE),
|
|
693
|
+
("duplicate-disposition-evidence", "disposition", "accepted_tradeoff"),
|
|
694
|
+
):
|
|
695
|
+
duplicate_case = make_fixture(case_name)
|
|
696
|
+
duplicate_occurrence = duplicate_case["ledger"]["finding_classes"][0][
|
|
697
|
+
"occurrences"
|
|
698
|
+
][-1]
|
|
699
|
+
duplicate_disposition_evidence_key(duplicate_occurrence, field, first_value)
|
|
700
|
+
duplicate_result = invoke(case_name, duplicate_case["ledger"])
|
|
701
|
+
duplicate_key_cases.append(
|
|
702
|
+
(
|
|
703
|
+
case_name,
|
|
704
|
+
duplicate_result.returncode,
|
|
705
|
+
f"duplicate object key: {field}" in duplicate_result.stdout,
|
|
706
|
+
"Traceback" in duplicate_result.stdout,
|
|
707
|
+
duplicate_result.stdout,
|
|
708
|
+
)
|
|
709
|
+
)
|
|
710
|
+
assert [item[1:4] for item in duplicate_key_cases] == [
|
|
711
|
+
(1, True, False),
|
|
712
|
+
(1, True, False),
|
|
713
|
+
], duplicate_key_cases
|
|
714
|
+
|
|
715
|
+
wrong_receipt_evidence = make_fixture("wrong-receipt-evidence")
|
|
716
|
+
wrong_receipt_evidence_occurrence = wrong_receipt_evidence["ledger"][
|
|
717
|
+
"finding_classes"
|
|
718
|
+
][0]["occurrences"][-1]
|
|
719
|
+
mutate_disposition_evidence(
|
|
720
|
+
wrong_receipt_evidence_occurrence,
|
|
721
|
+
lambda evidence: evidence.update(receipt_sha256="f" * 64),
|
|
722
|
+
)
|
|
723
|
+
run(
|
|
724
|
+
"wrong-receipt-evidence",
|
|
725
|
+
wrong_receipt_evidence["ledger"],
|
|
726
|
+
1,
|
|
727
|
+
"does not bind its controller receipt",
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
wrong_finding_evidence = make_fixture("wrong-finding-evidence")
|
|
731
|
+
wrong_finding_evidence_occurrence = wrong_finding_evidence["ledger"][
|
|
732
|
+
"finding_classes"
|
|
733
|
+
][0]["occurrences"][-1]
|
|
734
|
+
mutate_disposition_evidence(
|
|
735
|
+
wrong_finding_evidence_occurrence,
|
|
736
|
+
lambda evidence: evidence.update(finding_sha256="f" * 64),
|
|
737
|
+
)
|
|
738
|
+
run(
|
|
739
|
+
"wrong-finding-evidence",
|
|
740
|
+
wrong_finding_evidence["ledger"],
|
|
741
|
+
1,
|
|
742
|
+
"does not bind its controller finding",
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
historical_open = make_fixture(
|
|
746
|
+
"historical-open",
|
|
747
|
+
receipt_findings=[[finding(1)], [finding(2)], []],
|
|
748
|
+
)
|
|
749
|
+
historical_open["ledger"]["finding_classes"][0]["occurrences"][0][
|
|
750
|
+
"disposition"
|
|
751
|
+
] = "open"
|
|
752
|
+
clear_disposition_evidence(
|
|
753
|
+
historical_open["ledger"]["finding_classes"][0]["occurrences"][0]
|
|
754
|
+
)
|
|
755
|
+
run("historical-open", historical_open["ledger"], 1, "unresolved finding occurrence")
|
|
756
|
+
|
|
757
|
+
historical_open_linked = make_fixture(
|
|
758
|
+
"historical-open-linked",
|
|
759
|
+
receipt_findings=[[finding(1)], [finding(2)], []],
|
|
760
|
+
)
|
|
761
|
+
historical_open_linked_occurrences = historical_open_linked["ledger"][
|
|
762
|
+
"finding_classes"
|
|
763
|
+
][0]["occurrences"]
|
|
764
|
+
historical_open_linked_occurrences[0]["disposition"] = "open"
|
|
765
|
+
clear_disposition_evidence(historical_open_linked_occurrences[0])
|
|
766
|
+
bind_disposition_evidence(
|
|
767
|
+
"historical-open-linked-disposition.json",
|
|
768
|
+
historical_open_linked_occurrences[1],
|
|
769
|
+
resolves=[
|
|
770
|
+
occurrence_ref(historical_open_linked_occurrences[0]),
|
|
771
|
+
occurrence_ref(historical_open_linked_occurrences[1]),
|
|
772
|
+
],
|
|
773
|
+
)
|
|
774
|
+
run(
|
|
775
|
+
"historical-open-linked",
|
|
776
|
+
historical_open_linked["ledger"],
|
|
777
|
+
0,
|
|
778
|
+
"ready_for_human_decision",
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
source_refuted_without_evidence = make_fixture("source-refuted-without-evidence")
|
|
782
|
+
source_refuted_without_evidence_occurrence = source_refuted_without_evidence["ledger"][
|
|
783
|
+
"finding_classes"
|
|
784
|
+
][0]["occurrences"][-1]
|
|
785
|
+
source_refuted_without_evidence_occurrence["disposition"] = "source_refuted"
|
|
786
|
+
clear_disposition_evidence(source_refuted_without_evidence_occurrence)
|
|
787
|
+
run(
|
|
788
|
+
"source-refuted-without-evidence",
|
|
789
|
+
source_refuted_without_evidence["ledger"],
|
|
790
|
+
1,
|
|
791
|
+
"disposition_evidence_file",
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
source_refuted = make_fixture("source-refuted")
|
|
795
|
+
source_refuted_occurrence = source_refuted["ledger"]["finding_classes"][0][
|
|
796
|
+
"occurrences"
|
|
797
|
+
][-1]
|
|
798
|
+
source_refuted_occurrence["disposition"] = "source_refuted"
|
|
799
|
+
bind_disposition_evidence(
|
|
800
|
+
"source-refuted-evidence.json",
|
|
801
|
+
source_refuted_occurrence,
|
|
802
|
+
evidence=["synthetic first-hand source evidence"],
|
|
803
|
+
)
|
|
804
|
+
run("source-refuted", source_refuted["ledger"], 0, "ready_for_human_decision")
|
|
805
|
+
|
|
806
|
+
source_refuted_bad_hash = copy.deepcopy(source_refuted["ledger"])
|
|
807
|
+
source_refuted_bad_hash["finding_classes"][0]["occurrences"][-1][
|
|
808
|
+
"disposition_evidence_sha256"
|
|
809
|
+
] = "f" * 64
|
|
810
|
+
run(
|
|
811
|
+
"source-refuted-bad-hash",
|
|
812
|
+
source_refuted_bad_hash,
|
|
813
|
+
1,
|
|
814
|
+
"digest does not match",
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
needs_human = make_fixture("needs-human")
|
|
818
|
+
needs_human["ledger"]["finding_classes"][0]["occurrences"][-1][
|
|
819
|
+
"disposition"
|
|
820
|
+
] = "needs_human_decision"
|
|
821
|
+
clear_disposition_evidence(
|
|
822
|
+
needs_human["ledger"]["finding_classes"][0]["occurrences"][-1]
|
|
823
|
+
)
|
|
824
|
+
run("needs-human", needs_human["ledger"], 1, "unresolved finding occurrence")
|
|
825
|
+
|
|
826
|
+
third_occurrence = make_fixture(
|
|
827
|
+
"third-occurrence",
|
|
828
|
+
receipt_findings=[[finding(1)], [finding(2)], [finding(3)]],
|
|
829
|
+
completion=False,
|
|
830
|
+
unmatched=1,
|
|
831
|
+
open_last=True,
|
|
832
|
+
)
|
|
833
|
+
run("third-occurrence", third_occurrence["ledger"], 0, "continuation_authorization_required")
|
|
834
|
+
|
|
835
|
+
split_predicate = copy.deepcopy(third_occurrence["ledger"])
|
|
836
|
+
original_class = split_predicate["finding_classes"][0]
|
|
837
|
+
predicate_variants = [
|
|
838
|
+
"A closeout field is accepted without controller evidence.",
|
|
839
|
+
"a closeout field is accepted without controller evidence.",
|
|
840
|
+
"A closeout field is accepted without controller evidence.",
|
|
841
|
+
]
|
|
842
|
+
split_predicate["finding_classes"] = [
|
|
843
|
+
{
|
|
844
|
+
"key": f"receipt-binding-{index}",
|
|
845
|
+
"root_cause_predicate": predicate_variants[index - 1],
|
|
846
|
+
"affected_surface": original_class["affected_surface"],
|
|
847
|
+
"occurrences": [copy.deepcopy(occurrence)],
|
|
848
|
+
"authoritative_sweep": None,
|
|
849
|
+
}
|
|
850
|
+
for index, occurrence in enumerate(original_class["occurrences"], start=1)
|
|
851
|
+
]
|
|
852
|
+
run(
|
|
853
|
+
"split-predicate",
|
|
854
|
+
split_predicate,
|
|
855
|
+
1,
|
|
856
|
+
"duplicate normalized root_cause_predicate",
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
distinct_predicates = copy.deepcopy(split_predicate)
|
|
860
|
+
for index, row in enumerate(distinct_predicates["finding_classes"], start=1):
|
|
861
|
+
row["root_cause_predicate"] = (
|
|
862
|
+
f"Distinct controller-evidence failure predicate {index}."
|
|
863
|
+
)
|
|
864
|
+
run(
|
|
865
|
+
"distinct-predicates",
|
|
866
|
+
distinct_predicates,
|
|
867
|
+
0,
|
|
868
|
+
"closeout_state=continuation_authorization_required",
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
missing_sweep = copy.deepcopy(third_occurrence["ledger"])
|
|
872
|
+
missing_sweep["finding_classes"][0]["authoritative_sweep"] = None
|
|
873
|
+
run("missing-sweep", missing_sweep, 1, "authoritative_sweep")
|
|
874
|
+
|
|
875
|
+
bad_sweep_set = copy.deepcopy(third_occurrence["ledger"])
|
|
876
|
+
bad_sweep_set["finding_classes"][0]["authoritative_sweep"]["searched_set"] = [
|
|
877
|
+
"skills/code-review"
|
|
878
|
+
]
|
|
879
|
+
run("bad-sweep-set", bad_sweep_set, 1, "searched_set does not match")
|
|
880
|
+
|
|
881
|
+
ready_unmatched = make_fixture(
|
|
882
|
+
"ready-unmatched",
|
|
883
|
+
receipt_findings=[[finding(1), finding(2), finding(3)], []],
|
|
884
|
+
unmatched=1,
|
|
885
|
+
)
|
|
886
|
+
run("ready-unmatched", ready_unmatched["ledger"], 1, "ready sweep must report zero unmatched")
|
|
887
|
+
|
|
888
|
+
wrong_remote = make_fixture("wrong-remote")
|
|
889
|
+
wrong_remote["ledger"]["base_attestations"][1]["remote"] = "upstream"
|
|
890
|
+
run("wrong-remote", wrong_remote["ledger"], 1, "same remote and ref")
|
|
891
|
+
|
|
892
|
+
bad_sequence = make_fixture("bad-sequence")
|
|
893
|
+
bad_sequence["ledger"]["base_attestations"][1]["sequence"] = 3
|
|
894
|
+
run("bad-sequence", bad_sequence["ledger"], 1, "sequence must be contiguous")
|
|
895
|
+
|
|
896
|
+
bad_time = make_fixture("bad-time")
|
|
897
|
+
bad_time["ledger"]["base_attestations"][1]["confirmed_at"] = "2026-08-29T12:01:00Z"
|
|
898
|
+
run("bad-time", bad_time["ledger"], 1, "strictly increase")
|
|
899
|
+
|
|
900
|
+
invalid_time = make_fixture("invalid-time")
|
|
901
|
+
invalid_time["ledger"]["base_attestations"][0]["confirmed_at"] = "2026-08-29 12:00:00"
|
|
902
|
+
run("invalid-time", invalid_time["ledger"], 1, "strict RFC3339")
|
|
903
|
+
|
|
904
|
+
missing_mapping = make_fixture("missing-mapping")
|
|
905
|
+
missing_mapping["ledger"]["base_attestations"][1]["controller_receipt_sha256"] = None
|
|
906
|
+
run("missing-mapping", missing_mapping["ledger"], 1, "map every controller receipt")
|
|
907
|
+
|
|
908
|
+
consumed_first_drift = make_fixture("consumed-first-drift", base_shas=[A, B])
|
|
909
|
+
run(
|
|
910
|
+
"consumed-first-drift",
|
|
911
|
+
consumed_first_drift["ledger"],
|
|
912
|
+
0,
|
|
913
|
+
"closeout_state=ready_for_human_decision",
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
stable_trailing_recheck = make_fixture(
|
|
917
|
+
"stable-trailing-recheck", base_shas=[A, A, A]
|
|
918
|
+
)
|
|
919
|
+
run(
|
|
920
|
+
"stable-trailing-recheck",
|
|
921
|
+
stable_trailing_recheck["ledger"],
|
|
922
|
+
0,
|
|
923
|
+
"closeout_state=ready_for_human_decision",
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
unconsumed_first_drift = make_fixture(
|
|
927
|
+
"unconsumed-first-drift", base_shas=[A, A, B]
|
|
928
|
+
)
|
|
929
|
+
run(
|
|
930
|
+
"unconsumed-first-drift",
|
|
931
|
+
unconsumed_first_drift["ledger"],
|
|
932
|
+
1,
|
|
933
|
+
"final controller receipt does not consume the latest attested base",
|
|
934
|
+
)
|
|
935
|
+
|
|
936
|
+
bad_evidence = make_fixture("bad-evidence")
|
|
937
|
+
bad_evidence["ledger"]["base_attestations"][0]["evidence_sha256"] = "f" * 64
|
|
938
|
+
run("bad-evidence", bad_evidence["ledger"], 1, "digest does not match")
|
|
939
|
+
|
|
940
|
+
bad_evidence_content = make_fixture("bad-evidence-content")
|
|
941
|
+
bad_evidence_row = bad_evidence_content["ledger"]["base_attestations"][0]
|
|
942
|
+
bad_evidence_path = root / bad_evidence_row["evidence_file"]
|
|
943
|
+
bad_evidence_path.write_text(f"{A}\trefs/heads/other\n", encoding="utf-8")
|
|
944
|
+
bad_evidence_row["evidence_sha256"] = hashlib.sha256(
|
|
945
|
+
bad_evidence_path.read_bytes()
|
|
946
|
+
).hexdigest()
|
|
947
|
+
run(
|
|
948
|
+
"bad-evidence-content",
|
|
949
|
+
bad_evidence_content["ledger"],
|
|
950
|
+
1,
|
|
951
|
+
"canonical ls-remote output",
|
|
952
|
+
)
|
|
953
|
+
|
|
954
|
+
surrogate = make_fixture("surrogate")
|
|
955
|
+
surrogate["ledger"]["controller_review_state"] = "\ud800"
|
|
956
|
+
run("surrogate", surrogate["ledger"], 1, "valid UTF-8 text")
|
|
957
|
+
|
|
958
|
+
fifo = make_fixture("fifo")
|
|
959
|
+
fifo_name = "controller-receipt.fifo"
|
|
960
|
+
os.mkfifo(root / fifo_name)
|
|
961
|
+
fifo["ledger"]["controller_receipts"][0]["file"] = fifo_name
|
|
962
|
+
fifo["ledger"]["controller_receipts"][0]["sha256"] = "f" * 64
|
|
963
|
+
run("fifo", fifo["ledger"], 1, "singly linked regular file")
|
|
964
|
+
|
|
965
|
+
second_drift_wrong = make_fixture("second-drift-wrong", base_shas=[A, B, A])
|
|
966
|
+
run("second-drift-wrong", second_drift_wrong["ledger"], 1, "second base drift")
|
|
967
|
+
|
|
968
|
+
flip_flop = make_fixture(
|
|
969
|
+
"flip-flop",
|
|
970
|
+
completion=False,
|
|
971
|
+
base_shas=[A, B, A],
|
|
972
|
+
closeout="baseline_race",
|
|
973
|
+
)
|
|
974
|
+
flip_flop["ledger"]["unreviewed_delta"] = ["target returned to its first SHA"]
|
|
975
|
+
run("flip-flop", flip_flop["ledger"], 0, "base_changes=2")
|
|
976
|
+
|
|
977
|
+
one_round_race = make_fixture(
|
|
978
|
+
"one-round-race",
|
|
979
|
+
receipt_findings=[[]],
|
|
980
|
+
completion=False,
|
|
981
|
+
base_shas=[A, B, C],
|
|
982
|
+
closeout="baseline_race",
|
|
983
|
+
)
|
|
984
|
+
one_round_race["ledger"]["unreviewed_delta"] = ["second target drift after round 1"]
|
|
985
|
+
run("one-round-race", one_round_race["ledger"], 0, "base_changes=2")
|
|
986
|
+
|
|
987
|
+
post_race_round = make_fixture(
|
|
988
|
+
"post-race-round",
|
|
989
|
+
completion=False,
|
|
990
|
+
base_shas=[A, B, C],
|
|
991
|
+
closeout="baseline_race",
|
|
992
|
+
)
|
|
993
|
+
post_race_round["ledger"]["base_attestations"][1][
|
|
994
|
+
"controller_receipt_sha256"
|
|
995
|
+
] = None
|
|
996
|
+
post_race_round["ledger"]["base_attestations"][2][
|
|
997
|
+
"controller_receipt_sha256"
|
|
998
|
+
] = post_race_round["receipt_hashes"][1]
|
|
999
|
+
post_race_round["ledger"]["unreviewed_delta"] = [
|
|
1000
|
+
"round 2 was started after the second target drift"
|
|
1001
|
+
]
|
|
1002
|
+
run(
|
|
1003
|
+
"post-race-round",
|
|
1004
|
+
post_race_round["ledger"],
|
|
1005
|
+
1,
|
|
1006
|
+
"controller receipt is mapped at or after the second base drift",
|
|
1007
|
+
)
|
|
1008
|
+
|
|
1009
|
+
race_with_complete = make_fixture(
|
|
1010
|
+
"race-with-complete", base_shas=[A, B, C], closeout="baseline_race"
|
|
1011
|
+
)
|
|
1012
|
+
race_with_complete["ledger"]["unreviewed_delta"] = ["second target drift"]
|
|
1013
|
+
run("race-with-complete", race_with_complete["ledger"], 1, "cannot carry a completion receipt")
|
|
1014
|
+
|
|
1015
|
+
race_without_delta = make_fixture(
|
|
1016
|
+
"race-without-delta",
|
|
1017
|
+
completion=False,
|
|
1018
|
+
base_shas=[A, B, C],
|
|
1019
|
+
closeout="baseline_race",
|
|
1020
|
+
)
|
|
1021
|
+
run("race-without-delta", race_without_delta["ledger"], 1, "non-empty unreviewed_delta")
|
|
1022
|
+
|
|
1023
|
+
race_candidate = make_fixture(
|
|
1024
|
+
"race-candidate",
|
|
1025
|
+
completion=False,
|
|
1026
|
+
base_shas=[A, B, C],
|
|
1027
|
+
closeout="baseline_race",
|
|
1028
|
+
)
|
|
1029
|
+
race_candidate["ledger"]["candidate_sha256"] = OTHER_CANDIDATE
|
|
1030
|
+
race_candidate["ledger"]["unreviewed_delta"] = ["second target drift"]
|
|
1031
|
+
run(
|
|
1032
|
+
"race-candidate",
|
|
1033
|
+
race_candidate["ledger"],
|
|
1034
|
+
1,
|
|
1035
|
+
"controller receipt 1 does not bind the ledger candidate",
|
|
1036
|
+
)
|
|
1037
|
+
|
|
1038
|
+
false_race = make_fixture(
|
|
1039
|
+
"false-race", completion=False, closeout="baseline_race"
|
|
1040
|
+
)
|
|
1041
|
+
run("false-race", false_race["ledger"], 1, "baseline_race requires two ordered base changes")
|
|
1042
|
+
|
|
1043
|
+
needs_human_resolution_results = []
|
|
1044
|
+
for closing_disposition in sorted(CLOSED_DISPOSITIONS):
|
|
1045
|
+
case_name = f"historical-needs-human-{closing_disposition.replace('_', '-')}"
|
|
1046
|
+
historical_needs_human_linked = make_fixture(
|
|
1047
|
+
case_name,
|
|
1048
|
+
receipt_findings=[[finding(1)], [finding(2)], []],
|
|
1049
|
+
)
|
|
1050
|
+
historical_needs_human_occurrences = historical_needs_human_linked["ledger"][
|
|
1051
|
+
"finding_classes"
|
|
1052
|
+
][0]["occurrences"]
|
|
1053
|
+
historical_needs_human_occurrences[0]["disposition"] = "needs_human_decision"
|
|
1054
|
+
clear_disposition_evidence(historical_needs_human_occurrences[0])
|
|
1055
|
+
historical_needs_human_occurrences[1]["disposition"] = closing_disposition
|
|
1056
|
+
bind_disposition_evidence(
|
|
1057
|
+
f"{case_name}-disposition.json",
|
|
1058
|
+
historical_needs_human_occurrences[1],
|
|
1059
|
+
resolves=[
|
|
1060
|
+
occurrence_ref(historical_needs_human_occurrences[0]),
|
|
1061
|
+
occurrence_ref(historical_needs_human_occurrences[1]),
|
|
1062
|
+
],
|
|
1063
|
+
)
|
|
1064
|
+
needs_human_resolution_results.append(
|
|
1065
|
+
(case_name, invoke(case_name, historical_needs_human_linked["ledger"]))
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
duplicate_ledger = make_fixture("duplicate-ledger")
|
|
1069
|
+
duplicate_ledger_result = invoke_encoded(
|
|
1070
|
+
"duplicate-ledger",
|
|
1071
|
+
encode_duplicate_key(duplicate_ledger["ledger"], "schema_version", 2),
|
|
1072
|
+
)
|
|
1073
|
+
|
|
1074
|
+
duplicate_controller = make_fixture("duplicate-controller")
|
|
1075
|
+
duplicate_controller_ledger = duplicate_controller["ledger"]
|
|
1076
|
+
duplicate_controller_name = duplicate_controller_ledger["controller_receipts"][-1][
|
|
1077
|
+
"file"
|
|
1078
|
+
]
|
|
1079
|
+
duplicate_controller_payload = json.loads(
|
|
1080
|
+
(root / duplicate_controller_name).read_text(encoding="utf-8")
|
|
1081
|
+
)
|
|
1082
|
+
duplicate_controller_hash = write_duplicate_json(
|
|
1083
|
+
duplicate_controller_name,
|
|
1084
|
+
duplicate_controller_payload,
|
|
1085
|
+
"status",
|
|
1086
|
+
"findings",
|
|
1087
|
+
)
|
|
1088
|
+
duplicate_controller_ledger["controller_receipts"][-1][
|
|
1089
|
+
"sha256"
|
|
1090
|
+
] = duplicate_controller_hash
|
|
1091
|
+
duplicate_controller_ledger["base_attestations"][-1][
|
|
1092
|
+
"controller_receipt_sha256"
|
|
1093
|
+
] = duplicate_controller_hash
|
|
1094
|
+
duplicate_controller_complete_name = duplicate_controller_ledger[
|
|
1095
|
+
"completion_receipt"
|
|
1096
|
+
]["file"]
|
|
1097
|
+
duplicate_controller_complete = json.loads(
|
|
1098
|
+
(root / duplicate_controller_complete_name).read_text(encoding="utf-8")
|
|
1099
|
+
)
|
|
1100
|
+
duplicate_controller_complete[
|
|
1101
|
+
"completion_review_result_sha256"
|
|
1102
|
+
] = duplicate_controller_hash
|
|
1103
|
+
duplicate_controller_ledger["completion_receipt"]["sha256"] = write_json(
|
|
1104
|
+
duplicate_controller_complete_name,
|
|
1105
|
+
duplicate_controller_complete,
|
|
1106
|
+
)
|
|
1107
|
+
duplicate_controller_result = invoke(
|
|
1108
|
+
"duplicate-controller",
|
|
1109
|
+
duplicate_controller_ledger,
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
duplicate_completion = make_fixture("duplicate-completion")
|
|
1113
|
+
duplicate_completion_ledger = duplicate_completion["ledger"]
|
|
1114
|
+
duplicate_completion_name = duplicate_completion_ledger["completion_receipt"]["file"]
|
|
1115
|
+
duplicate_completion_payload = json.loads(
|
|
1116
|
+
(root / duplicate_completion_name).read_text(encoding="utf-8")
|
|
1117
|
+
)
|
|
1118
|
+
duplicate_completion_ledger["completion_receipt"]["sha256"] = write_duplicate_json(
|
|
1119
|
+
duplicate_completion_name,
|
|
1120
|
+
duplicate_completion_payload,
|
|
1121
|
+
"review_state",
|
|
1122
|
+
"reviewed",
|
|
1123
|
+
)
|
|
1124
|
+
duplicate_completion_result = invoke(
|
|
1125
|
+
"duplicate-completion",
|
|
1126
|
+
duplicate_completion_ledger,
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
duplicate_sweep = make_fixture(
|
|
1130
|
+
"duplicate-sweep",
|
|
1131
|
+
receipt_findings=[[finding(1)], [finding(2)], [finding(3)]],
|
|
1132
|
+
completion=False,
|
|
1133
|
+
open_last=True,
|
|
1134
|
+
)
|
|
1135
|
+
duplicate_sweep_ledger = duplicate_sweep["ledger"]
|
|
1136
|
+
duplicate_sweep_row = duplicate_sweep_ledger["finding_classes"][0][
|
|
1137
|
+
"authoritative_sweep"
|
|
1138
|
+
]
|
|
1139
|
+
duplicate_sweep_payload = json.loads(
|
|
1140
|
+
(root / duplicate_sweep_row["manifest_file"]).read_text(encoding="utf-8")
|
|
1141
|
+
)
|
|
1142
|
+
duplicate_sweep_row["manifest_sha256"] = write_duplicate_json(
|
|
1143
|
+
duplicate_sweep_row["manifest_file"],
|
|
1144
|
+
duplicate_sweep_payload,
|
|
1145
|
+
"candidate_sha256",
|
|
1146
|
+
OTHER_CANDIDATE,
|
|
1147
|
+
)
|
|
1148
|
+
duplicate_sweep_result = invoke("duplicate-sweep", duplicate_sweep_ledger)
|
|
1149
|
+
|
|
1150
|
+
new_regressions = [
|
|
1151
|
+
*[
|
|
1152
|
+
(
|
|
1153
|
+
case_name,
|
|
1154
|
+
result,
|
|
1155
|
+
"cannot resolve a needs_human_decision occurrence",
|
|
1156
|
+
)
|
|
1157
|
+
for case_name, result in needs_human_resolution_results
|
|
1158
|
+
],
|
|
1159
|
+
("duplicate-ledger", duplicate_ledger_result, "duplicate object key: schema_version"),
|
|
1160
|
+
("duplicate-controller", duplicate_controller_result, "duplicate object key: status"),
|
|
1161
|
+
(
|
|
1162
|
+
"duplicate-completion",
|
|
1163
|
+
duplicate_completion_result,
|
|
1164
|
+
"duplicate object key: review_state",
|
|
1165
|
+
),
|
|
1166
|
+
("duplicate-sweep", duplicate_sweep_result, "duplicate object key: candidate_sha256"),
|
|
1167
|
+
]
|
|
1168
|
+
assert [
|
|
1169
|
+
(name, result.returncode, token in result.stdout, "Traceback" in result.stdout)
|
|
1170
|
+
for name, result, token in new_regressions
|
|
1171
|
+
] == [(name, 1, True, False) for name, _result, _token in new_regressions], [
|
|
1172
|
+
(name, result.returncode, result.stdout) for name, result, _token in new_regressions
|
|
1173
|
+
]
|
|
1174
|
+
|
|
1175
|
+
print("test_validate_extraction_review_state: ok")
|
|
1176
|
+
PY
|