diff --git a/.github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml b/.github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml new file mode 100644 index 00000000..65163f65 --- /dev/null +++ b/.github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml @@ -0,0 +1,732 @@ +name: Workflow Delivery v3 Buddy smoke destination acceptance retry 4 + +on: + workflow_dispatch: + inputs: + target_sha: + description: Reviewed protected-finalization target SHA + required: true + default: '0000000000000000000000000000000000000000' + type: string + package_coordinate: + description: Fixed disposable GitHub Packages base coordinate + required: true + default: '@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13' + type: string + confirm: + description: Explicit disposable-probe confirmation + required: true + default: I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4 + type: string + +permissions: {} + +concurrency: + group: hcoona-release-smoke-npm-workflow-delivery-v3-buddy-smoke-acceptance-retry-4 + cancel-in-progress: false + +env: + WDV3_ACCEPTANCE_TARGET_SHA: '0000000000000000000000000000000000000000' + WDV3_ACCEPTANCE_PACKAGE_COORDINATE: '@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13' + WDV3_ACCEPTANCE_CONFIRMATION: I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4 + WDV3_ACCEPTANCE_REF: refs/heads/main + WDV3_PURPOSE: destination-acceptance + +jobs: + validate-fixed-inputs: + name: Validate protected fixed acceptance inputs + if: ${{ github.run_attempt == 1 }} + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read + steps: + - name: Fail closed before review or mutation + shell: bash + env: + INPUT_TARGET_SHA: ${{ inputs.target_sha }} + INPUT_PACKAGE_COORDINATE: ${{ inputs.package_coordinate }} + INPUT_CONFIRM: ${{ inputs.confirm }} + run: | + if [[ "${INPUT_TARGET_SHA}" != "${WDV3_ACCEPTANCE_TARGET_SHA}" ]]; then + exit 1 + fi + if [[ "${WDV3_ACCEPTANCE_TARGET_SHA}" == "0000000000000000000000000000000000000000" ]]; then + exit 1 + fi + if [[ "${INPUT_TARGET_SHA}" == "0000000000000000000000000000000000000000" ]]; then + exit 1 + fi + if [[ "${INPUT_PACKAGE_COORDINATE}" != "${WDV3_ACCEPTANCE_PACKAGE_COORDINATE}" ]]; then + exit 1 + fi + if [[ "${INPUT_CONFIRM}" != "${WDV3_ACCEPTANCE_CONFIRMATION}" ]]; then + exit 1 + fi + if [[ "${GITHUB_REF}" != "${WDV3_ACCEPTANCE_REF}" ]]; then + exit 1 + fi + if [[ "${GITHUB_RUN_ATTEMPT}" != "1" ]]; then + exit 1 + fi + + acceptance-review: + name: Review fixed destination acceptance + if: ${{ github.run_attempt == 1 }} + needs: validate-fixed-inputs + runs-on: ubuntu-24.04 + timeout-minutes: 30 + permissions: {} + environment: workflow-delivery-v3-buddy-smoke-acceptance-retry-4 + outputs: + artifact-id: ${{ steps.upload.outputs.artifact-id }} + steps: + - name: Materialize immutable review coordinates + shell: bash + env: + INPUT_TARGET_SHA: ${{ inputs.target_sha }} + INPUT_PACKAGE_COORDINATE: ${{ inputs.package_coordinate }} + run: | + mkdir -p .wdv3 + printf '%s\n' \ + "purpose=${WDV3_PURPOSE}" \ + "target-sha=${INPUT_TARGET_SHA}" \ + "package-coordinate=${INPUT_PACKAGE_COORDINATE}" \ + "workflow=${GITHUB_WORKFLOW_REF}" \ + "run=${GITHUB_RUN_ID}" \ + "run-attempt=${GITHUB_RUN_ATTEMPT}" \ + > ".wdv3/acceptance-review-r${GITHUB_RUN_ID}-ra${GITHUB_RUN_ATTEMPT}.txt" + - name: Upload immutable acceptance review coordinates + id: upload + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: wdv3-acceptance-review-r${{ github.run_id }}-ra${{ github.run_attempt }} + path: .wdv3/acceptance-review-r${{ github.run_id }}-ra${{ github.run_attempt }}.txt + if-no-files-found: error + include-hidden-files: true + retention-days: 45 + overwrite: false + archive: false + + probe-absent-create-readback: + name: Probe absent create and exact readback + if: ${{ github.run_attempt == 1 }} + needs: acceptance-review + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: read + packages: write + outputs: + result: ${{ steps.classify.outputs.result }} + mutation-classification: ${{ steps.classify.outputs.mutation-classification }} + scenario-inventory: ${{ steps.classify.outputs.scenario-inventory }} + record-digest: ${{ steps.classify.outputs.record-digest }} + record-json: ${{ steps.classify.outputs.record-json }} + artifact-id: ${{ steps.upload.outputs.artifact-id }} + artifact-digest: ${{ steps.upload.outputs.artifact-digest }} + steps: + - name: Check out reviewed target + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ inputs.target_sha }} + persist-credentials: false + - name: Install uv + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + version: '0.12.5' + github-token: '' + - name: Install exact Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: '24.19.0' + - name: Install and verify exact npm toolchain + shell: bash + run: | + npm install --global npm@11.17.0 + test "$(node --version)" = "v24.19.0" + test "$(npm --version)" = "11.17.0" + - name: Run fixed absent/create/readback suite + id: probe + env: + WDV3_ACCEPTANCE_GITHUB_TOKEN: ${{ github.token }} + INPUT_PACKAGE_COORDINATE: ${{ inputs.package_coordinate }} + INPUT_TARGET_SHA: ${{ inputs.target_sha }} + shell: bash + run: | + uv run --python 3.13 --package three-workflow-delivery-v3 \ + three-workflow-delivery-v3 governance run-fixed-acceptance-probe \ + --suite absent-create-readback \ + --package-coordinate "${INPUT_PACKAGE_COORDINATE}" \ + --target-sha "${INPUT_TARGET_SHA}" \ + --timeout-seconds 120 \ + --output ".wdv3/probe-absent-r${GITHUB_RUN_ID}-ra${GITHUB_RUN_ATTEMPT}.json" \ + --github-output "${GITHUB_OUTPUT}" + - name: Upload immutable absent/create/readback suite + id: upload + if: ${{ always() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: wdv3-acceptance-probe-absent-r${{ github.run_id }}-ra${{ github.run_attempt }} + path: .wdv3/probe-absent-r${{ github.run_id }}-ra${{ github.run_attempt }}.json + if-no-files-found: error + include-hidden-files: true + retention-days: 45 + overwrite: false + archive: false + - name: Classify absent/create/readback completion + id: classify + if: ${{ always() }} + shell: bash + env: + PROBE_OUTCOME: ${{ steps.probe.outcome }} + PROBE_RESULT: ${{ steps.probe.outputs.result }} + PROBE_MUTATION_CLASSIFICATION: ${{ steps.probe.outputs.mutation-classification }} + PROBE_SCENARIO_INVENTORY: ${{ steps.probe.outputs.scenario-inventory }} + PROBE_RECORD_DIGEST: ${{ steps.probe.outputs.record-digest }} + PROBE_RECORD_JSON: ${{ steps.probe.outputs.record-json }} + UPLOAD_OUTCOME: ${{ steps.upload.outcome }} + UPLOAD_ARTIFACT_ID: ${{ steps.upload.outputs.artifact-id }} + UPLOAD_ARTIFACT_DIGEST: ${{ steps.upload.outputs.artifact-digest }} + run: | + result="${PROBE_RESULT:-unknown}" + classification="${PROBE_MUTATION_CLASSIFICATION:-unknown}" + if [ "${PROBE_OUTCOME}" != "success" ] || [ "${UPLOAD_OUTCOME}" != "success" ]; then + result=unknown + classification=unknown + elif [ -z "${UPLOAD_ARTIFACT_ID}" ] || [ -z "${UPLOAD_ARTIFACT_DIGEST}" ]; then + result=incomplete + classification=incomplete + elif [ "${result}" != "success" ] || [ "${classification}" != "complete" ]; then + case "${classification}" in + incomplete|unknown) result="${classification}" ;; + *) result=unknown; classification=unknown ;; + esac + fi + { + printf 'result=%s\n' "${result}" + printf 'mutation-classification=%s\n' "${classification}" + printf 'scenario-inventory=%s\n' "${PROBE_SCENARIO_INVENTORY}" + printf 'record-digest=%s\n' "${PROBE_RECORD_DIGEST}" + printf 'record-json=%s\n' "${PROBE_RECORD_JSON}" + } >> "${GITHUB_OUTPUT}" + - name: Require complete absent/create/readback evidence + if: ${{ always() }} + shell: bash + env: + MUTATION_CLASSIFICATION: ${{ steps.classify.outputs.mutation-classification }} + run: test "${MUTATION_CLASSIFICATION}" = "complete" + + probe-exact-and-conflict: + name: Probe exact, races, and lost response + if: ${{ github.run_attempt == 1 && needs.probe-absent-create-readback.result == 'success' }} + needs: probe-absent-create-readback + runs-on: ubuntu-24.04 + timeout-minutes: 15 + permissions: + contents: read + packages: write + outputs: + result: ${{ steps.classify.outputs.result }} + mutation-classification: ${{ steps.classify.outputs.mutation-classification }} + scenario-inventory: ${{ steps.classify.outputs.scenario-inventory }} + record-digest: ${{ steps.classify.outputs.record-digest }} + record-json: ${{ steps.classify.outputs.record-json }} + artifact-id: ${{ steps.upload.outputs.artifact-id }} + artifact-digest: ${{ steps.upload.outputs.artifact-digest }} + steps: + - name: Check out reviewed target + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ inputs.target_sha }} + persist-credentials: false + - name: Install uv + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + version: '0.12.5' + github-token: '' + - name: Install exact Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: '24.19.0' + - name: Install and verify exact npm toolchain + shell: bash + run: | + npm install --global npm@11.17.0 + test "$(node --version)" = "v24.19.0" + test "$(npm --version)" = "11.17.0" + - name: Run fixed exact and conflict suite + id: probe + env: + WDV3_ACCEPTANCE_GITHUB_TOKEN: ${{ github.token }} + INPUT_PACKAGE_COORDINATE: ${{ inputs.package_coordinate }} + INPUT_TARGET_SHA: ${{ inputs.target_sha }} + shell: bash + run: | + uv run --python 3.13 --package three-workflow-delivery-v3 \ + three-workflow-delivery-v3 governance run-fixed-acceptance-probe \ + --suite exact-and-conflict \ + --package-coordinate "${INPUT_PACKAGE_COORDINATE}" \ + --target-sha "${INPUT_TARGET_SHA}" \ + --timeout-seconds 300 \ + --output ".wdv3/probe-conflict-r${GITHUB_RUN_ID}-ra${GITHUB_RUN_ATTEMPT}.json" \ + --github-output "${GITHUB_OUTPUT}" + - name: Upload immutable exact/conflict suite + id: upload + if: ${{ always() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: wdv3-acceptance-probe-conflict-r${{ github.run_id }}-ra${{ github.run_attempt }} + path: .wdv3/probe-conflict-r${{ github.run_id }}-ra${{ github.run_attempt }}.json + if-no-files-found: error + include-hidden-files: true + retention-days: 45 + overwrite: false + archive: false + - name: Classify exact/conflict completion + id: classify + if: ${{ always() }} + shell: bash + env: + PROBE_OUTCOME: ${{ steps.probe.outcome }} + PROBE_RESULT: ${{ steps.probe.outputs.result }} + PROBE_MUTATION_CLASSIFICATION: ${{ steps.probe.outputs.mutation-classification }} + PROBE_SCENARIO_INVENTORY: ${{ steps.probe.outputs.scenario-inventory }} + PROBE_RECORD_DIGEST: ${{ steps.probe.outputs.record-digest }} + PROBE_RECORD_JSON: ${{ steps.probe.outputs.record-json }} + UPLOAD_OUTCOME: ${{ steps.upload.outcome }} + UPLOAD_ARTIFACT_ID: ${{ steps.upload.outputs.artifact-id }} + UPLOAD_ARTIFACT_DIGEST: ${{ steps.upload.outputs.artifact-digest }} + run: | + result="${PROBE_RESULT:-unknown}" + classification="${PROBE_MUTATION_CLASSIFICATION:-unknown}" + if [ "${PROBE_OUTCOME}" != "success" ] || [ "${UPLOAD_OUTCOME}" != "success" ]; then + result=unknown + classification=unknown + elif [ -z "${UPLOAD_ARTIFACT_ID}" ] || [ -z "${UPLOAD_ARTIFACT_DIGEST}" ]; then + result=incomplete + classification=incomplete + elif [ "${result}" != "success" ] || [ "${classification}" != "complete" ]; then + case "${classification}" in + incomplete|unknown) result="${classification}" ;; + *) result=unknown; classification=unknown ;; + esac + fi + { + printf 'result=%s\n' "${result}" + printf 'mutation-classification=%s\n' "${classification}" + printf 'scenario-inventory=%s\n' "${PROBE_SCENARIO_INVENTORY}" + printf 'record-digest=%s\n' "${PROBE_RECORD_DIGEST}" + printf 'record-json=%s\n' "${PROBE_RECORD_JSON}" + } >> "${GITHUB_OUTPUT}" + - name: Require complete exact/conflict evidence + if: ${{ always() }} + shell: bash + env: + MUTATION_CLASSIFICATION: ${{ steps.classify.outputs.mutation-classification }} + run: test "${MUTATION_CLASSIFICATION}" = "complete" + + capture-governance-evidence: + name: Capture terminal Governance acceptance evidence + if: ${{ always() && github.run_attempt == 1 }} + needs: + - validate-fixed-inputs + - acceptance-review + - probe-absent-create-readback + - probe-exact-and-conflict + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: read + steps: + - name: Check out immutable workflow source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ github.workflow_sha }} + persist-credentials: false + token: ${{ github.token }} + - name: Install uv + uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + version: '0.12.5' + github-token: '' + - name: Form and admit terminal Governance evidence + shell: bash + env: + INPUT_TARGET_SHA: ${{ inputs.target_sha }} + INPUT_PACKAGE_COORDINATE: ${{ inputs.package_coordinate }} + VALIDATE_RESULT: ${{ needs.validate-fixed-inputs.result }} + REVIEW_RESULT: ${{ needs.acceptance-review.result }} + ABSENT_JOB_RESULT: ${{ needs.probe-absent-create-readback.result }} + CONFLICT_JOB_RESULT: ${{ needs.probe-exact-and-conflict.result }} + REVIEW_ARTIFACT_ID: ${{ needs.acceptance-review.outputs.artifact-id }} + ABSENT_RESULT: ${{ needs.probe-absent-create-readback.outputs.result }} + ABSENT_MUTATION_CLASSIFICATION: ${{ needs.probe-absent-create-readback.outputs.mutation-classification }} + ABSENT_SCENARIO_INVENTORY: ${{ needs.probe-absent-create-readback.outputs.scenario-inventory }} + ABSENT_RECORD_JSON: ${{ needs.probe-absent-create-readback.outputs.record-json }} + ABSENT_RECORD_DIGEST: ${{ needs.probe-absent-create-readback.outputs.record-digest }} + ABSENT_ARTIFACT_ID: ${{ needs.probe-absent-create-readback.outputs.artifact-id }} + ABSENT_ARTIFACT_DIGEST: ${{ needs.probe-absent-create-readback.outputs.artifact-digest }} + CONFLICT_RESULT: ${{ needs.probe-exact-and-conflict.outputs.result }} + CONFLICT_MUTATION_CLASSIFICATION: ${{ needs.probe-exact-and-conflict.outputs.mutation-classification }} + CONFLICT_SCENARIO_INVENTORY: ${{ needs.probe-exact-and-conflict.outputs.scenario-inventory }} + CONFLICT_RECORD_JSON: ${{ needs.probe-exact-and-conflict.outputs.record-json }} + CONFLICT_RECORD_DIGEST: ${{ needs.probe-exact-and-conflict.outputs.record-digest }} + CONFLICT_ARTIFACT_ID: ${{ needs.probe-exact-and-conflict.outputs.artifact-id }} + CONFLICT_ARTIFACT_DIGEST: ${{ needs.probe-exact-and-conflict.outputs.artifact-digest }} + run: | + mkdir -p .wdv3 + export WDV3_FILE=".wdv3/governance-acceptance-r${GITHUB_RUN_ID}-ra${GITHUB_RUN_ATTEMPT}.json" + # Terminal fan-in source bindings: + # needs.validate-fixed-inputs.result + # needs.acceptance-review.result + # needs.probe-absent-create-readback.result + # needs.probe-exact-and-conflict.result + # needs.probe-absent-create-readback.outputs.record-digest + # needs.probe-exact-and-conflict.outputs.record-digest + # Optional record-json and artifact-id/artifact-digest outputs are + # parsed independently so missing artifact outputs cannot crash. + # needs.probe-absent-create-readback.result == 'failure' => possibly-started + # needs.probe-absent-create-readback.result == 'cancelled' => possibly-started + # needs.probe-absent-create-readback.result == 'skipped' => not-started + # needs.probe-exact-and-conflict.result == 'failure' => possibly-started + # needs.probe-exact-and-conflict.result == 'cancelled' => possibly-started + # needs.probe-exact-and-conflict.result == 'skipped' => not-started + uv run --python 3.13 --package three-workflow-delivery-v3 python - <<'PY' + import hashlib + import json + import os + import re + from pathlib import Path + + from three_workflow_delivery_v3.adapters.github_packages import ( + AcceptanceRunnerDiagnostic, + FixedAcceptanceSuiteResult, + FixedCoordinateAcceptanceProbeResult, + ValidatedAcceptanceRequestProof, + ) + from three_workflow_delivery_v3.canonical import canonical_sha256, canonicalize + from three_workflow_delivery_v3.records.governance import ( + admit_governance_acceptance_evidence, + ) + + expected = ( + ("probe-absent-create-readback", ("absent-create-readback",), "ABSENT"), + ( + "probe-exact-and-conflict", + ("exact", "identical-race", "differing-race", "lost-response"), + "CONFLICT", + ), + ) + ranks = {"complete": 0, "incomplete": 1, "unknown": 2} + asserted_results = { + "ABSENT": os.environ.get("ABSENT_RESULT", ""), + "CONFLICT": os.environ.get("CONFLICT_RESULT", ""), + } + asserted_classifications = { + "ABSENT": os.environ.get("ABSENT_MUTATION_CLASSIFICATION", ""), + "CONFLICT": os.environ.get("CONFLICT_MUTATION_CLASSIFICATION", ""), + } + asserted_inventories = { + "ABSENT": os.environ.get("ABSENT_SCENARIO_INVENTORY", ""), + "CONFLICT": os.environ.get("CONFLICT_SCENARIO_INVENTORY", ""), + } + asserted_records = { + "ABSENT": os.environ.get("ABSENT_RECORD_JSON", ""), + "CONFLICT": os.environ.get("CONFLICT_RECORD_JSON", ""), + } + asserted_record_digests = { + "ABSENT": os.environ.get("ABSENT_RECORD_DIGEST", ""), + "CONFLICT": os.environ.get("CONFLICT_RECORD_DIGEST", ""), + } + asserted_artifact_ids = { + "ABSENT": os.environ.get("ABSENT_ARTIFACT_ID", ""), + "CONFLICT": os.environ.get("CONFLICT_ARTIFACT_ID", ""), + } + asserted_artifact_digests = { + "ABSENT": os.environ.get("ABSENT_ARTIFACT_DIGEST", ""), + "CONFLICT": os.environ.get("CONFLICT_ARTIFACT_DIGEST", ""), + } + + def digest(value): + if not value: + return None + normalized = value if value.startswith("sha256:") else f"sha256:{value}" + return normalized if re.fullmatch(r"sha256:[0-9a-f]{64}", normalized) else None + + def positive_or_none(value): + try: + parsed = int(value) + except (TypeError, ValueError): + return None + return parsed if parsed > 0 else None + + def validated_proof_or_none(scenario): + proof = scenario.get("validated-request-proof") + if proof is None: + return None + if not isinstance(proof, dict): + raise ValueError("validated request proof is not an object") + return ValidatedAcceptanceRequestProof.from_closed_document( + proof, + package_coordinate=scenario["package-coordinate"], + tag=scenario["tag"], + response_identity_digest=scenario["response"]["identity-digest"], + ) + + def runner_diagnostic_or_none(scenario): + diagnostic = scenario.get("runner-diagnostic") + if diagnostic is None: + return None + if not isinstance(diagnostic, dict): + raise ValueError("runner diagnostic is not an object") + return AcceptanceRunnerDiagnostic( + exit_classification=diagnostic["exit-classification"], + upstream_status=diagnostic["upstream-status"], + exception_category=diagnostic["exception-category"], + request_correlation_digest=diagnostic[ + "request-correlation-digest" + ], + ) + + def suite_fact(probe, inventory, prefix, job_result): + asserted_result = asserted_results[prefix] + asserted_classification = asserted_classifications[prefix] + asserted_inventory = asserted_inventories[prefix] + raw = asserted_records[prefix] + record_json = None + record_digest = None + scenarios = [] + probe_result = "incomplete" + if raw: + try: + record_json = json.loads(raw) + if not isinstance(record_json, dict): + raise ValueError("suite record is not an object") + if tuple(record_json.get("scenario-inventory", ())) != inventory: + raise ValueError("suite scenario inventory mismatch") + if asserted_inventory and json.loads( + asserted_inventory + ) != list(inventory): + raise ValueError("suite output inventory mismatch") + scenario_results = tuple( + FixedCoordinateAcceptanceProbeResult( + scenario=scenario["scenario"], + package_coordinate=scenario["package-coordinate"], + tag=scenario["tag"], + pre_state=scenario["pre"]["state"], + post_state=scenario["post"]["state"], + result=scenario["response"]["result"], + mutation_classification=scenario["mutation-classification"], + action_executed=scenario["action"]["executed"], + mutation_started=scenario["action"][ + "mutation-started" + ], + response_identity_digest=scenario["response"]["identity-digest"], + content_sha512=scenario["post"]["content-sha512"], + diagnostics=tuple(scenario["response"]["diagnostics"]), + validated_request_proof=validated_proof_or_none(scenario), + runner_diagnostic=runner_diagnostic_or_none(scenario), + ) + for scenario in record_json["scenarios"] + ) + suite = FixedAcceptanceSuiteResult( + suite=probe.removeprefix("probe-"), + scenarios=scenario_results, + ) + record_digest = suite.to_document()["record-digest"] + asserted_digest = asserted_record_digests[prefix] + if ( + record_digest != asserted_digest + or record_digest != record_json.get("record-digest") + or tuple(suite.scenario_inventory) != inventory + ): + raise ValueError("suite record digest mismatch") + scenarios = record_json["scenarios"] + probe_result = suite.result + asserted_result_classification = ( + "complete" + if asserted_result == "success" + else asserted_result + ) + if ( + asserted_result_classification + and asserted_result_classification not in ranks + ): + raise ValueError("suite result output is not closed") + if ( + asserted_classification + and asserted_classification not in ranks + ): + raise ValueError( + "suite mutation classification output is not closed" + ) + if ( + asserted_result_classification + and asserted_classification + and asserted_result_classification + != asserted_classification + ): + raise ValueError( + "suite result and mutation classification outputs mismatch" + ) + for output_classification in ( + asserted_result_classification, + asserted_classification, + ): + if ( + output_classification + and ranks[output_classification] + < ranks[suite.mutation_classification] + ): + raise ValueError( + "suite outputs cannot upgrade canonical classification" + ) + except (KeyError, TypeError, ValueError, json.JSONDecodeError): + record_json = None + record_digest = None + scenarios = [] + probe_result = "unknown" + + artifact_id = positive_or_none(asserted_artifact_ids[prefix]) + artifact_digest = digest(asserted_artifact_digests[prefix]) + if record_json is not None and ( + artifact_id is None or artifact_digest is None + ): + probe_result = max( + probe_result if probe_result != "success" else "complete", + "incomplete", + key=ranks.__getitem__, + ) + if ( + job_result in {"failure", "cancelled"} + and record_json is None + ): + probe_result = "unknown" + elif job_result == "skipped": + probe_result = max( + probe_result if probe_result != "success" else "complete", + "incomplete", + key=ranks.__getitem__, + ) + elif not raw: + probe_result = "incomplete" + return { + "probe": probe, + "result": "success" if probe_result == "complete" else probe_result, + "scenario-inventory": list(inventory), + "record-digest": record_digest, + "artifact-id": artifact_id, + "artifact-digest": artifact_digest, + "scenarios": scenarios, + } + + dependencies = [ + {"job": "validate-fixed-inputs", "result": os.environ["VALIDATE_RESULT"]}, + {"job": "acceptance-review", "result": os.environ["REVIEW_RESULT"]}, + {"job": "probe-absent-create-readback", "result": os.environ["ABSENT_JOB_RESULT"]}, + {"job": "probe-exact-and-conflict", "result": os.environ["CONFLICT_JOB_RESULT"]}, + ] + job_results = { + "ABSENT": os.environ["ABSENT_JOB_RESULT"], + "CONFLICT": os.environ["CONFLICT_JOB_RESULT"], + } + probe_facts = [ + suite_fact(*item, job_results[item[2]]) for item in expected + ] + classification = "complete" + for dependency in dependencies: + if dependency["job"] in { + "probe-absent-create-readback", + "probe-exact-and-conflict", + } and dependency["result"] in {"failure", "cancelled"}: + classification = "unknown" + elif dependency["result"] != "success": + classification = max( + classification, + "incomplete", + key=ranks.__getitem__, + ) + for fact in probe_facts: + probe_classification = ( + "complete" if fact["result"] == "success" else fact["result"] + ) + classification = max( + classification, + probe_classification, + key=ranks.__getitem__, + ) + review_artifact_id = positive_or_none(os.environ.get("REVIEW_ARTIFACT_ID")) + if review_artifact_id is None: + classification = max( + classification, + "incomplete", + key=ranks.__getitem__, + ) + document = { + "schema": "workflow-delivery/v3/governance-acceptance-evidence", + "purpose": "destination-acceptance", + "workflow": { + "repository": os.environ["GITHUB_REPOSITORY"], + "path": ".github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml", + "ref": os.environ["GITHUB_REF"], + "sha": os.environ["GITHUB_WORKFLOW_SHA"], + }, + "target-sha": ( + os.environ.get( + "INPUT_TARGET_SHA", + os.environ.get( + "WDV3_ACCEPTANCE_TARGET_SHA", + "0000000000000000000000000000000000000000", + ), + ) + if os.environ["VALIDATE_RESULT"] == "success" + else os.environ.get( + "WDV3_ACCEPTANCE_TARGET_SHA", + "0000000000000000000000000000000000000000", + ) + ), + "package-coordinate": os.environ.get( + "WDV3_ACCEPTANCE_PACKAGE_COORDINATE", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + ), + "confirmation-digest": "sha256:" + hashlib.sha256( + os.environ["WDV3_ACCEPTANCE_CONFIRMATION"].encode() + ).hexdigest(), + "environment": "workflow-delivery-v3-buddy-smoke-acceptance-retry-4", + "reviewer": {"login": None, "source": "unavailable-in-job-context"}, + "recovery": { + "workflow-run-id": int(os.environ["GITHUB_RUN_ID"]), + "environment": "workflow-delivery-v3-buddy-smoke-acceptance-retry-4", + "deployment": f"run:{os.environ['GITHUB_RUN_ID']}/environment:acceptance", + "job": "acceptance-review", + "artifact-id": review_artifact_id, + }, + "dependency-results": dependencies, + "probe-facts": probe_facts, + "mutation-classification": classification, + "producer": "capture-governance-evidence", + "workflow-run-id": int(os.environ["GITHUB_RUN_ID"]), + "run-attempt": int(os.environ["GITHUB_RUN_ATTEMPT"]), + "release-lineage": "none", + } + admitted = admit_governance_acceptance_evidence(canonicalize(document)) + + def write_evidence(): + Path(os.environ["WDV3_FILE"]).write_bytes( + canonicalize(admitted.to_document()) + ) + + write_evidence() + PY + - name: Upload immutable Governance acceptance evidence + if: ${{ always() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: wdv3-governance-acceptance-r${{ github.run_id }}-ra${{ github.run_attempt }} + path: .wdv3/governance-acceptance-r${{ github.run_id }}-ra${{ github.run_attempt }}.json + if-no-files-found: error + include-hidden-files: true + retention-days: 45 + overwrite: false + archive: false diff --git a/.testagent/plan.md b/.testagent/plan.md index 24ec5522..cb76c714 100644 --- a/.testagent/plan.md +++ b/.testagent/plan.md @@ -1,5 +1,238 @@ # Workflow Delivery v3 Snapshot Admission Test Plan +## 2026-08-28 Workflow Delivery v3 Retry-4 Acceptance Preparation Plan + +### Strategy and edit boundary + +Use one sequential **Research -> Plan -> Implement** pass. The result is +intentionally RED: test collection and test-side quality gates must pass, but +execution must fail solely because the fourth production profiles and the +retry-4 workflow do not yet exist. + +Allowed implementation paths: + +1. `src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py` +2. `src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py` +3. `src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py` +4. `src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py` +5. `src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py` + +The `.testagent` artifacts are also allowed. Do not edit production Python, +the CLI, manifests, locks, Governance configuration, or `.github/workflows`. +Do not create, restore, or mutate a workflow. Add no skip, `xfail`, +conditional escape, or weakened assertion. + +### Phase 1 - Adapter identity, resolution, runner, suite, and proof + +Modify only +`tests/adapters/test_commit10_acceptance_probes.py`. + +Before adding retry-4 expectations, move the two valid-form unregistered +negative fixtures from `.13` to `.17`: + +- keep + `test_retry_2_suite_resolves_only_the_reviewed_coordinate_block`; +- keep + `test_acceptance_probe_requires_the_fixed_coordinate_and_explicit_tag`; +- alter only fixture coordinates/tags and preserve the negative assertions. + +Add these collection-safe tests: + +1. `test_retry_4_adapter_profiles_have_stable_historical_order_and_unique_base_coordinates` + - exactly `.1`, `.5`, `.9`, `.13`, stable and unique. +2. `test_retry_4_adapter_profiles_preserve_scenario_order_and_qualified_identity_uniqueness` + - exact five-scenario order for every profile; + - unique profile-qualified identity; + - only intentional absent/exact reuse inside a profile. +3. `test_retry_4_adapter_coordinate_tag_pairs_are_exact_and_globally_unique` + - exactly 16 coordinate/tag pairs across four blocks; + - exact retry-4 `.13`, `.13`, `.14`, `.15`, `.16` mapping and tags. +4. `test_retry_4_fixed_acceptance_resolvers_return_exact_scenarios_and_coordinates` + - direct fourth-profile resolution and `.17` rejection. +5. `test_retry_4_npm_runner_invokes_all_five_exact_coordinates` + - record exact five runner coordinates in order using the existing fake. +6. `test_retry_4_fixed_acceptance_suite_routes_exact_bindings_through_controlled_fakes` + - observe exact package/base/scenario/coordinate/tag routing and suite + result. +7. `test_retry_4_validated_proof_accepts_exact_coordinate_tag_bindings` + - matched retry-4 proof. +8. `test_retry_4_validated_proof_rejects_historical_substitutions_in_both_directions` + - retry-4/historical coordinate-tag substitutions both ways. + +Run Adapter collection first, then the two retained negative tests, then +`-k retry_4`. Collection and the retained tests must pass. Retry-4 execution +must fail only as `E-ADAPTER-PROFILE-ABSENT`; correct any collection, +fixture, fake, or assertion-construction defect without touching source. + +Maps: A1, A3-A12. + +### Phase 2 - Governance preparation and placeholder-finalized shapes + +Modify only +`tests/governance/test_commit10_acceptance_evidence.py`. + +Add test-local retry-4 document helpers and: + +1. `test_retry_4_governance_profiles_have_stable_historical_order_and_unique_base_coordinates` +2. `test_retry_4_governance_profile_binds_exact_workflow_environment_confirmation_digest_and_scenarios` +3. `test_retry_4_governance_admits_exact_zero_target_rejected_dispatch` +4. `test_retry_4_governance_rejects_non_exact_zero_targets` + - 39 zeroes, 41 zeroes, non-ASCII zeroes, and nonzero hex. +5. `test_retry_4_zero_target_rejects_review_probe_record_artifact_reviewer_or_mutation_claims` + - one precise forbidden observable per parameter case. +6. `test_retry_4_finalized_placeholder_round_trips_canonically_with_exact_bindings` + - temporarily patch the expected fourth Governance profile with a clearly + named test-only nonzero 40-hex target; + - never use the work-base or provenance SHA as authority. +7. `test_retry_4_governance_rejects_cross_profile_field_substitutions` + - workflow, Environment, recovery Environment, digest, target, coordinate, + and tag in both applicable directions. +8. `test_retry_4_governance_preserves_historical_profiles_digests_and_replay_evidence` + - exact retry-1 through retry-3 tuples, admission, suite digests, and replay + evidence. + +The exact zero preparation case must assert validation failure; skipped +review/probes; empty records; absent artifact and reviewer; and incomplete +mutation classification. Run collection, historical-preservation selections, +then `-k retry_4`. Classify only absence of the fourth profile as +`E-GOVERNANCE-PROFILE-ABSENT`. + +Maps: A2-A3 and G1-G10. + +### Phase 3 - Dedicated retry-4 workflow contract + +Add +`tests/contracts/test_commit10_acceptance_retry_4_workflow.py`. +Re-author the assertions against current authority. Historical retry-3 tests +may supply mechanisms only. Load the absent workflow lazily inside test +bodies, making absence an ordinary assertion failure rather than a collection +or fixture error. + +Add: + +1. `test_retry_4_workflow_uses_exact_temporary_path_stem_and_environment_identity` +2. `test_retry_4_workflow_declares_exact_five_jobs_in_order` +3. `test_retry_4_workflow_applies_first_attempt_guards_and_terminal_always_capture` +4. `test_retry_4_workflow_scopes_environment_and_packages_write_permissions_to_exact_jobs` +5. `test_retry_4_workflow_zero_target_stops_before_review_and_write_capable_probes` +6. `test_retry_4_workflow_test_only_nonzero_placeholder_satisfies_finalized_guard_shape` +7. `test_retry_4_workflow_dispatch_identity_confirmation_digest_and_concurrency_are_exact` +8. `test_retry_4_workflow_pins_current_actions_toolchains_checkout_and_probe_wiring` +9. `test_retry_4_workflow_wires_terminal_governance_evidence_exactly` +10. `test_retry_4_workflow_rejects_wrong_dispatch_inputs` +11. `test_retry_4_workflow_exposes_no_live_release_bypass_force_or_generalized_triggers` + +The tests must require exactly: + +- `validate-fixed-inputs`; +- `acceptance-review`; +- `probe-absent-create-readback`; +- `probe-exact-and-conflict`; +- `capture-governance-evidence`. + +Require first-attempt guards, terminal `always()` capture, Environment only on +review, and `packages: write` only on the two probe jobs. Execute the bounded +guard script in controlled test environments to prove that exactly forty +zeroes fail before review or either mutation-capable probe, while a clearly +test-only nonzero target demonstrates the eventual finalized shape. + +Run collection, then the file. Collection must pass; all execution failures +must be `E-WORKFLOW-ABSENT`. + +Maps: W1-W7. + +### Phase 4 - Preparation topology and retirement exception + +Modify only the terminal bounded topology tests in: + +- `tests/contracts/test_buddy_workflows.py`; +- `tests/contracts/test_commit11_legacy_buddy_retirement.py`. + +Use exact, non-generalized tests: + +1. `test_retry_4_is_the_only_required_temporary_acceptance_workflow_during_preparation` +2. `test_retry_4_preparation_keeps_normal_buddy_disabled_and_live_enabled_false` +3. `test_retry_4_is_the_only_temporary_workflow_allowed_by_legacy_buddy_retirement` +4. `test_legacy_buddy_retirement_rejects_every_other_temporary_and_legacy_workflow` + +Require the retry-4 path, reject every additional temporary identity, and +explicitly preserve absence of original, retry-2, and retry-3. Keep normal +Buddy disabled and `live_enabled: false`. + +Run collection, then the three contract paths with +`-k 'retry_4 or temporary_acceptance'`. Only workflow absence may fail, as +`E-WORKFLOW-ABSENT`; all negative topology cases must pass. + +Maps: T1-T2. + +### Phase 5 - Quality and expected-RED validation + +After all test edits: + +1. Invoke `test-gap-analysis` against the two production targets and all five + bounded test files. Resolve only test-side gaps; production absence remains + intentionally out of scope. +2. Invoke `assertion-quality` against all five test files. Replace any + truthiness-only, self-derived, tautological, broad-exception, or + single-observable assertion. +3. Re-open the final tests and map every checklist item to concrete + assertions. +4. Run five-file collection. It must pass. +5. Run existing non-retry-4/non-temporary selections. They must pass. +6. Run Ruff check and format check on all five paths. +7. Run `uv build --package three-workflow-delivery-v3`. +8. Run `git --no-pager diff --check`, scope audit, and explicitly verify the + retry-4 workflow remains absent. +9. Run the combined five-file + `-k 'retry_4 or temporary_acceptance'` command last. It must exit nonzero + only with: + - `E-ADAPTER-PROFILE-ABSENT`; + - `E-GOVERNANCE-PROFILE-ABSENT`; + - `E-WORKFLOW-ABSENT`. +10. Record exact commands, exit codes, failing node IDs, observed messages, + classifications, unexpected defects, and the no-production/no-external + mutation audit in `.testagent/status.md`. + +Maps: S1-S4. + +### Requirement traceability + +| Requirement | Planned evidence | +|---|---| +| A1 | Adapter stable-order/unique-base test | +| A2 | Governance stable-order/unique-base test | +| A3 | Both stable-order/unique-base tests | +| A4-A5 | Adapter scenario-order/qualified-identity test | +| A6-A7 | Adapter exact/global coordinate-tag test | +| A8 | Adapter resolver test | +| A9 | Adapter npm-runner test | +| A10 | Adapter controlled-suite test | +| A11 | Adapter exact-proof and bidirectional-substitution tests | +| A12 | Two retained Adapter negative tests using `.17` | +| G1-G3 | Governance exact binding test | +| G4-G6 | Governance exact/non-exact zero and forbidden-observable tests | +| G7-G8 | Governance placeholder-finalized round-trip test | +| G9 | Governance cross-profile substitution test | +| G10 | Governance historical-preservation test and non-retry-4 baseline | +| W1-W7 | Dedicated workflow module, one exact test per mechanism above | +| T1 | Exact Buddy topology and retirement exception tests | +| T2 | Disabled-Buddy/`live_enabled: false` test | +| S1-S2 | Diff/scope/no-workflow/no-external-mutation audit in status | +| S3 | Collection, unrelated baseline, and final expected-RED records | +| S4 | Per-phase failure ledger in status | + +### Completion criteria + +- Each allowed test path is owned by exactly one phase. +- All A/G/W/T/S checklist items have concrete evidence. +- Collection, retained historical behavior, lint, format, build, and diff + checks pass. +- Mandatory test-gap and assertion-quality reviews have no unresolved + test-side findings. +- The combined run is RED only for absent retry-4 profiles/workflow. +- Zero skips/xfails, zero production edits, zero workflow edits, and zero + external mutations. + ## 2026-08-13 Commit 8 Governance Observation Error Taxonomy Plan ### Strategy @@ -6061,3 +6294,27 @@ reuse of consumed acceptance coordinates. gates. + + + +## PR #608 retry-4 terminal fixed-identity plan + +1. Add exactly + `test_retry_4_terminal_program_preserves_fixed_identity_after_rejected_dispatch` + to the existing retry-4 workflow contract module. +2. Extract `_terminal_python(document)`, run it with `sys.executable`, and + write `WDV3_FILE` beneath `tmp_path`. Supply failed validation, skipped + review/probe dependencies, empty optional outputs, wrong dispatch values, + fixed retry-4 constants, first attempt, positive run ID, exact repository + and ref, and a valid nonzero workflow SHA. +3. Admit the written bytes with + `admit_governance_acceptance_evidence`; assert exact fixed identity, + dependency results, incomplete classification, absent reviewer/artifacts/ + scenarios, and run attempt. The wrong target makes the test reject the + `INPUT_TARGET_SHA`-after-failure mutant. +4. Run the exact new node, the whole retry-4 contract module, Ruff check, Ruff + format check, and focused Pyrefly. Then run scoped `test-gap-analysis` and + `assertion-quality`, re-run affected validation if strengthened, and audit + the four-file allowlist and append-only notes. + + diff --git a/.testagent/research.md b/.testagent/research.md index 03c23f23..e79d7b55 100644 --- a/.testagent/research.md +++ b/.testagent/research.md @@ -1,5 +1,269 @@ # Workflow Delivery v3 Snapshot Admission Research +## 2026-08-28 Workflow Delivery v3 Retry-4 Acceptance Preparation Research + +### Scope and authority + +- Strategy: **single-pass Research -> Plan -> Implement**, tests-first and + deliberately RED. +- Workspace: + `/workspace/three-workspaces/design-workflows`. +- Branch: `workflow-delivery-v3-acceptance-retry-4`. +- Authoritative base and current HEAD: + `bcf47e2d817b718adf96a67ef0506d220b74f2bf`. +- The current v3 handoff was read first: + `docs/wiki/analyses/workflow-delivery/v3/agent-handoff.md`. +- The current workspace is authoritative. Historical retry-3 files may be + consulted only as mechanism references. They must not be restored. +- `bf1748971f2717a8877852590c5436b4160a4fbf` is implementation provenance + only; neither it nor the work-base SHA is an acceptable reviewed target. +- This phase may edit only tests, test-local fixtures/contracts, and these + `.testagent` records. It must not edit production Python registries or add + the workflow. + +### Language and repository conventions + +- Python 3.13, pytest 9.1.1, a UV workspace, and Hatchling. +- Package manifest: + `src/public/lib/three-workflow-delivery-v3/pyproject.toml`. +- Root pytest configuration uses importlib import mode and includes the v3 + tests in `testpaths`. +- Existing tests use plain `test_*` functions, bare concrete assertions, + descriptive parameter IDs, `pytest.raises(..., match=...)`, exact + tuple/dictionary equality, canonical JSON bytes, `tmp_path`, + `monkeypatch`, controlled fake transports/runners, and `yaml.safe_load`. +- Missing retry-4 production symbols and the absent workflow must be accessed + from test bodies so collection remains green and execution is behaviorally + RED. +- The `code-testing-extensions` entry point was unavailable; the local base + extension at + `.agents/skills/code-testing-extensions/extensions/python.md` was read. + Its normal green-suite rule is superseded here by the explicit tests-first + request to preserve expected RED failures. + +### Approved retry-4 identity and binding contract + +- Package: `@hcoona/hcoona-release-smoke-npm`. +- Stable scenario order and exact bindings: + 1. `absent-create-readback` -> + `0.0.0-wdv3-acceptance.13`, `wdv3-acceptance-13`; + 2. `exact` -> `0.0.0-wdv3-acceptance.13`, + `wdv3-acceptance-13`; + 3. `identical-race` -> `0.0.0-wdv3-acceptance.14`, + `wdv3-acceptance-14`; + 4. `differing-race` -> `0.0.0-wdv3-acceptance.15`, + `wdv3-acceptance-15`; + 5. `lost-response` -> `0.0.0-wdv3-acceptance.16`, + `wdv3-acceptance-16`. +- Fourth base coordinate: + `@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13`. +- Workflow path: + `.github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml`. +- Workflow stem and Environment: + `workflow-delivery-v3-buddy-smoke-acceptance-retry-4`. +- Confirmation: + `I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4`. +- Confirmation digest: + `sha256:b6f94d3c13c98b0714404959dd878230f8302ee849038a536f5a18cc3a85c7ec`. +- Preparation target: + `0000000000000000000000000000000000000000`, exactly forty ASCII + zeroes. +- `.1` through `.12` and every historical identity remain consumed. +- `live_enabled` remains `false`. + +### Bounded target inventory + +Test files to modify: + +1. `src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py` + - fourth Adapter profile order/uniqueness; + - resolution and exact coordinates; + - suite behavior using existing fakes; + - `_AcceptanceNpmRunner` coordinates; + - matched proof and bidirectional cross-profile rejection; + - move two `.13` negative fixtures to clearly unregistered `.17`. +2. `src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py` + - fourth Governance profile order/uniqueness; + - exact zero-sentinel preparation admission; + - fail-closed zero shape; + - placeholder-only finalized target shape; + - exact workflow, Environment, confirmation, digest, coordinate, and tag + bindings; + - cross-profile rejection and historical evidence preservation. +3. `src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py` + - permit and require exactly the retry-4 temporary workflow during + preparation while normal Buddy remains disabled. +4. `src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py` + - permit exactly retry-4 and continue rejecting every other temporary or + legacy workflow. + +Test file to add: + +5. `src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py` + - dedicated retry-4 workflow contract re-authored from current authority + and historical retry-3 mechanisms. + +Read-only behavior targets: + +- `src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/adapters/github_packages.py` + (`_ACCEPTANCE_SUITE_PROFILES`, + `_ACCEPTANCE_COORDINATE_TAG_PAIRS`, + `fixed_acceptance_scenario_specs`, `fixed_acceptance_coordinates`, + `ValidatedAcceptanceRequestProof`, and `run_fixed_acceptance_suite`). +- `src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/records/governance.py` + (`_GovernanceAcceptanceProfile`, + `_GOVERNANCE_ACCEPTANCE_PROFILES`, `_acceptance_profile`, + `_require_zero_target_rejected_dispatch`, and + `admit_governance_acceptance_evidence`). +- `src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/cli.py` + (`_AcceptanceNpmRunner` and the existing fixed-suite CLI route); this is a + consumer only and requires no CLI change. +- The absent retry-4 workflow path above; production workflow creation is + forbidden in this phase. +- `.github/workflow-delivery/governance/hcoona-release-smoke-npm.json`; + `live_enabled: false` is read-only. + +### Existing fixture conflict + +The now-approved `.13` coordinate occurs in two current Adapter negative +fixtures and must move without weakening their assertions: + +1. `test_retry_2_suite_resolves_only_the_reviewed_coordinate_block`, near + line 242: replace the unreviewed base `.13` with `.17`. +2. `test_acceptance_probe_requires_the_fixed_coordinate_and_explicit_tag`, + near lines 2529-2530: replace coordinate/tag `.13` with + `.17`/`wdv3-acceptance-17`. + +`.17` is syntactically valid and clearly outside all four registered blocks. + +### Requirement checklist + +- [ ] A1: Adapter has exactly four profiles in stable historical order + `.1`, `.5`, `.9`, `.13`. +- [ ] A2: Governance has exactly four profiles in the same stable order. +- [ ] A3: All four base identities are unique. +- [ ] A4: Every profile retains exact five-scenario order. +- [ ] A5: Profile-qualified base/scenario/coordinate/tag identities are + unique, with only the intentional absent/exact reuse inside each profile. +- [ ] A6: The four four-version blocks yield 16 unique accepted + coordinate/tag pairs. +- [ ] A7: Retry-4 uses exactly `.13`/`.13`/`.14`/`.15`/`.16` and tags + `-13`/`-13`/`-14`/`-15`/`-16`. +- [ ] A8: `fixed_acceptance_scenario_specs` and + `fixed_acceptance_coordinates` resolve the fourth profile. +- [ ] A9: `_AcceptanceNpmRunner` uses all five exact retry-4 coordinates. +- [ ] A10: `run_fixed_acceptance_suite` routes the exact retry-4 + base/coordinate/tag values through controlled fakes. +- [ ] A11: A matched retry-4 proof is accepted and retry-4/historical + coordinate-tag substitutions are rejected in both directions. +- [ ] A12: Both formerly negative `.13` fixtures move to `.17` while their + negative assertions remain unchanged. +- [ ] G1: Governance binds the exact retry-4 workflow path and Environment. +- [ ] G2: Governance binds the exact confirmation literal and digest. +- [ ] G3: Governance binds the exact `.13` through `.16` scenario + coordinates/tags. +- [ ] G4: Preparation target is exactly forty ASCII zeroes. +- [ ] G5: Only an exact zero-target rejected-dispatch shape is admitted: + validation failed; review and both probes skipped; no probe records, + artifact, or reviewer; mutation classification incomplete. +- [ ] G6: Zero-target documents implying review, retained scenarios, or + possible mutation are rejected. +- [ ] G7: Eventual finalized shape is represented only with a clearly named + test-local 40-hex placeholder reviewed target and temporary registry patch; + the placeholder is not authority. +- [ ] G8: The placeholder finalized shape round-trips canonically with exact + retry-4 scenario/proof bindings where representable. +- [ ] G9: Cross-profile workflow path, Environment, recovery Environment, + digest, target, coordinate, and tag substitutions are rejected. +- [ ] G10: Retry-1 through retry-3 admission, profile tuples, suite digests, + and replay evidence remain unchanged. +- [ ] W1: A dedicated retry-4 static workflow contract is modeled on + historical retry-3 mechanics but re-authored against current source. +- [ ] W2: It requires exactly five jobs: + `validate-fixed-inputs`, `acceptance-review`, + `probe-absent-create-readback`, `probe-exact-and-conflict`, and + `capture-governance-evidence`. +- [ ] W3: It requires first-attempt guards, terminal `always()` capture, + Environment only on review, and `packages: write` only on the two probe + jobs. +- [ ] W4: The forty-zero target fails in validation before review or either + write-capable probe can run. +- [ ] W5: A test-only nonzero placeholder demonstrates the finalized guard + shape without assigning an actual final target. +- [ ] W6: Exact dispatch inputs, confirmation/digest, current pinned actions + and toolchains, concurrency, checkout, probe, and terminal evidence wiring + are fixed. +- [ ] W7: Wrong inputs fail closed and Live, Release, bypass, force, + schedule, push, `workflow_call`, and generalized routes are absent. +- [ ] T1: Topology and retirement permit exactly retry-4 during preparation; + original, retry-2, and retry-3 temporary workflow sources remain absent. +- [ ] T2: Disabled normal Buddy and `live_enabled: false` remain required. +- [ ] S1: No production registry, workflow, CLI schema, generalized profile + framework, generic architecture, manifest, lock, or external state change. +- [ ] S2: No Live, bypass, external workflow/Environment/package/ref + mutation, or historical file restoration. +- [ ] S3: Test collection stays green; execution is RED only because fourth + profiles and workflow behavior are absent. +- [ ] S4: Narrow runs record exact node IDs and classify expected missing + production/workflow behavior separately from accidental test defects. + +### Exact commands + +Run from the repository root. + +Collection: + +```text +uv run --python 3.13 --package three-workflow-delivery-v3 pytest --collect-only -q \ + src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py \ + src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py +``` + +Narrow Adapter, Governance, and workflow/topology RED runs: + +```text +uv run --python 3.13 --package three-workflow-delivery-v3 pytest -q \ + src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py \ + -k retry_4 + +uv run --python 3.13 --package three-workflow-delivery-v3 pytest -q \ + src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py \ + -k retry_4 + +uv run --python 3.13 --package three-workflow-delivery-v3 pytest -q \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py \ + -k 'retry_4 or temporary_acceptance' +``` + +Combined RED run: + +```text +uv run --python 3.13 --package three-workflow-delivery-v3 pytest -q \ + src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py \ + src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py \ + src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py \ + -k 'retry_4 or temporary_acceptance' +``` + +Quality checks: + +```text +uv run --python 3.13 ruff check +uv run --python 3.13 ruff format --check +uv build --package three-workflow-delivery-v3 +git --no-pager diff --check +``` + +The final scoped execution must remain RED. Collection, lint, formatting, +package build, and unrelated baseline behavior must not fail. + ## 2026-08-13 Commit 8 Governance Observation Error Taxonomy Research ### Scope and strategy @@ -5465,3 +5729,30 @@ implementation and the count-only-lock mutant while retaining the correct implementation. + + + +## PR #608 retry-4 terminal fixed-identity gap + +At authoritative HEAD `127131db0f1f06817ace20d0249cf7dffa0d84e9`, the +retry-4 workflow contract statically inspects the embedded terminal Python but +does not execute its rejected-dispatch branch. The bounded gap is one +subprocess test in +`tests/contracts/test_commit10_acceptance_retry_4_workflow.py`. + +Existing conventions provide `_terminal_python`, `sys.executable` subprocess +execution, `tmp_path`, bare exact assertions, and strict canonical admission. +The workflow/test constants fix the rejected identity to forty zeroes, +`@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13`, +`I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4`, and digest +`sha256:b6f94d3c13c98b0714404959dd878230f8302ee849038a536f5a18cc3a85c7ec`. + +Acceptance checklist: execute the exact extracted program with failed +validation, skipped downstream jobs, empty optional outputs, wrong dispatch +inputs, and fixed `WDV3_ACCEPTANCE_*` values; require successful canonical +write and admission; assert the complete rejected-dispatch identity and empty +mutation surfaces. This kills substitution of `INPUT_TARGET_SHA` in the +validation-failure branch. No production, workflow, or normative-document edit +is in scope. + + diff --git a/.testagent/status.md b/.testagent/status.md index f953a0d0..1c4b5866 100644 --- a/.testagent/status.md +++ b/.testagent/status.md @@ -9891,3 +9891,114 @@ The status update containing this result is staged and the same staged gate will be rerun over the final commit candidate. + + + +## Retry-4 destination-acceptance preparation + +The tests-first phase established the expected missing-profile and +missing-workflow failures before production implementation. The bounded +implementation then added the closed `.13` through `.16` Adapter and +Governance profile, the exact retry-4 temporary workflow, and no CLI schema +or generalized registry abstraction. + +The preparation target remains exactly forty zeroes. Local execution proves +that fixed-input validation fails before the protected Environment or either +`packages: write` probe can run. Terminal evidence binds the fixed +confirmation identity even when a dispatch input is rejected. + +Validation at `2026-08-28T06:18:02Z`: + +- focused retry-4 contract selection: `56 passed`; +- complete v3 suite: `3836 passed`; +- scoped Ruff, Ruff format, and Pyrefly: passed; +- package build and Actionlint: passed; +- unstaged HK before final review: passed; +- multi-reviewer OCR review: three findings, each independently adjudicated + true positive and repaired; +- second-round workflow, test/topology, and holistic reviews: `No findings.`; +- external workflow, Environment, package, tag, and acceptance-ref mutations: + none. + + + + + +## PR #608 retry-4 terminal fixed-identity closure + +Added exactly one test, +`test_retry_4_terminal_program_preserves_fixed_identity_after_rejected_dispatch`, +to the existing retry-4 workflow contract. It executes the exact +`_terminal_python(document)` string with `sys.executable`, writes beneath +`tmp_path`, admits the bytes, and pins the fixed rejected-dispatch identity +despite deliberately wrong dispatch target/package values. + +Validation: + +- exact new node: `1 passed in 1.72s`; +- whole retry-4 workflow contract module: `12 passed in 1.44s`; +- Ruff check: `All checks passed!` (an initial `I001` was corrected); +- Ruff format check: `1 file already formatted`; +- Pyrefly: `0 errors` (`2 warnings not shown`); +- complete v3 package suite: `3837 passed in 467.28s`. + +`test-gap-analysis` and `assertion-quality` were invoked for only the embedded +failure branch and new test. Their optional `test-analysis-extensions` +dependency was unavailable, so the scoped pytest review was completed inline. +The wrong nonzero `INPUT_TARGET_SHA` kills substitution of that value after +validation failure: the admitted target becomes the wrong nonzero value, so +the exact zero-target assertion fails. Wrong package/conditional confirmation +inputs, exact dependency/probe structures, canonical bytes, +reviewer/artifact/scenario absence, workflow identity, and run coordinates are +independently asserted. +The 13 assertions span subprocess outcome, file side effect, canonical bytes, +and deep concrete evidence; none is assertion-free, trivial-only, +self-referential, or tautological. + +Only the named test module and append-only EOF notes in +`.testagent/research.md`, `.testagent/plan.md`, and `.testagent/status.md` +changed. No production, workflow, normative documentation, expected count, or +external GitHub state was modified. + + + + + +## PR #608 terminal closure workspace correction + +The preceding "Only ... changed" statement applies only to the generated +terminal-test subphase. The final follow-up workspace also updates +`docs/wiki/analyses/workflow-delivery/v3/agent-handoff.md` and +`docs/wiki/log.md` so their recorded complete-suite count is 3,837 rather than +3,836. At final workspace scope, the follow-up therefore comprises the test +module, the three append-only `.testagent` notes, and those two documentation +count corrections. + +Independent adjudication also classified ambient subprocess-environment +inheritance as a true positive and mandatory network sandboxing as a false +positive. The executable test now uses Python isolated mode, an isolated home, +and a minimal allowlisted environment rather than forwarding credentials, +proxy configuration, `PYTHONPATH`, or other ambient process state. The tested +terminal program contains no network operation, so no nonportable network +namespace or generalized call sandbox was added. + + + + + +## PR #608 terminal closure final unstaged gate + +After the subprocess-environment and workspace-scope adjudications: + +- the exact rejected-dispatch executable test passed; +- the complete retry-4 workflow contract module passed all 12 tests; +- scoped Ruff, Ruff format, and Pyrefly checks passed; +- the complete v3 suite passed all 3,837 tests; +- `GIT_LFS_SKIP_SMUDGE=1 mise exec -- hk check --check --no-progress + --unstaged` passed over the six-file workspace diff, including its managed + 3,837-test v3 suite. + +No external GitHub Environment, acceptance ref, workflow dispatch, package, +tag, or Live mutation occurred during this closure. + + diff --git a/docs/wiki/analyses/workflow-delivery/v3/README.md b/docs/wiki/analyses/workflow-delivery/v3/README.md index ddc36b5a..b2708d91 100644 --- a/docs/wiki/analyses/workflow-delivery/v3/README.md +++ b/docs/wiki/analyses/workflow-delivery/v3/README.md @@ -229,18 +229,25 @@ Next: 1. preserve all three attempts and their exact evidence as unsuccessful historical replay; do not infer acceptance from destination state or the new diagnostics; -2. treat `bf174897` only as the validated PR #603 implementation-provenance - boundary; start successor work from a freshly fetched and revalidated - `origin/main` containing PR #606 merge `0cb88f29` or a later reviewed - successor, then build a tests-first fourth profile with a wholly new - four-coordinate block and new workflow, Environment, review, invocation, - tag, and ref identities; -3. revalidate the current owner, `main`, ruleset, checks, open-PR drift, runs, - deployments, refs, workflow identities, package versions, and tags before - protected preparation or any external mutation; -4. execute at most one new package-mutation attempt and stop for reconciliation - on any nonterminal or ambiguous result; and -5. keep `live_enabled: false`. Subsequent explicit user authorization covers +2. treat preparation PR #608 as a prerequisite: no later phase may begin + unless it has merged without bypass. It carries the closed `.13`-`.16` + profile, retry-4 workflow and Environment identity, and exact confirmation, + but keeps the target at 40 zeroes so validation stops before review or + mutation. The work started from freshly fetched `origin/main@bcf47e2d`; + `bf174897` remains implementation provenance only; +3. after preparation merges, revalidate the current principal, owner, `main`, + ruleset, checks, open-PR drift, runs, deployments, refs, workflow identity, + Environment, package versions, and tags. Only then create the fresh + protected retry-4 Environment; +4. open a separate finalization PR that changes the workflow target default and + constant, Governance profile target, and target-pinned tests to the + preparation merge SHA. The reviewed target is the preparation merge, not the + finalization commit; +5. after finalization merges without bypass, run a fresh exact preflight. Only + if it passes may any acceptance ref be created or the single + `run_attempt == 1` package-mutation attempt be dispatched; stop for + reconciliation on any nonterminal or ambiguous result; and +6. keep `live_enabled: false`. Subsequent explicit user authorization covers the bounded acceptance-only repair/retry loop through genuine success, cleanup, and closure, but normal Live activation remains a separate production decision. diff --git a/docs/wiki/analyses/workflow-delivery/v3/agent-handoff.md b/docs/wiki/analyses/workflow-delivery/v3/agent-handoff.md index 59dfd74d..8045c9a4 100644 --- a/docs/wiki/analyses/workflow-delivery/v3/agent-handoff.md +++ b/docs/wiki/analyses/workflow-delivery/v3/agent-handoff.md @@ -235,17 +235,39 @@ If it conflicts with the three consumed attempts successful and does not restore `.1`-`.12`. - Documentation closure PR #606 merged as `0cb88f2972296f3acf874015165c1e88f50c595c`. Treat `bf174897` only as the - validated PR #603 implementation-provenance boundary. Start successor work - from a freshly fetched and revalidated `origin/main` containing `0cb88f29` - or a later reviewed successor. Historical local branches and attempt refs - are cleanup or reconciliation inputs only; do not dispatch, resume, or use - them as work bases. -- Subsequent explicit user authorization covers the bounded acceptance-only - repair/retry loop through genuine success, reconciliation, cleanup, and - authoritative closure. A fourth attempt still requires a wholly new reviewed - four-coordinate profile, workflow, Environment, invocation, and identities. - Normal Live activation and `live_enabled: true` remain a separate production - decision and are not authorized. + validated PR #603 implementation-provenance boundary. Work-base clarification + PR #607 merged as `bcf47e2d817b718adf96a67ef0506d220b74f2bf`, and PR #608 + preparation started from freshly fetched and revalidated + `origin/main@bcf47e2d`. Every post-preparation phase must start from freshly + fetched and revalidated `origin/main` containing the PR #608 merge, or a + later reviewed successor that contains it. Historical local branches and + attempt refs are cleanup or reconciliation inputs only; do not dispatch, + resume, or use them as work bases. +- Preparation PR #608 now carries the tests-first fourth profile without + operating it. The closed profile uses base `.13`: absent/exact `.13`, + identical-race `.14`, differing-race `.15`, and lost-response `.16`, with + tags `wdv3-acceptance-13` through `wdv3-acceptance-16`; workflow + `.github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml`; + Environment `workflow-delivery-v3-buddy-smoke-acceptance-retry-4`; and + confirmation + `I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4`. The Governance and + workflow target remains exactly 40 zeroes, so fixed-input validation fails + before Environment review or either package-write probe. The complete v3 + suite passes 3,837 tests, local HK gates pass, and multi-reviewer review + converged to no findings after independent adjudication and repair. No + Environment, acceptance dispatch, package or tag mutation, or acceptance + ref was created. PR #608 is the preparation carrier; no later phase may + begin unless it has merged without bypass. Once that prerequisite is + satisfied, revalidate external state and create the fresh protected + Environment before a separate finalization PR pins the workflow and + Governance profile to the preparation merge SHA. The finalization PR must + also merge without bypass and a fresh exact preflight must still pass before + creating any acceptance ref or dispatching the single `run_attempt == 1` + attempt. Subsequent explicit user authorization covers the bounded + acceptance-only repair/retry loop through genuine success, reconciliation, + cleanup, and authoritative closure. Normal Live activation and + `live_enabled: true` remain a separate production decision and are not + authorized. - Requirements, HLD, and all five MLDs are confirmed. - Implementation commits 1 through 11 of the approved first-slice LLD are delivered. Commit 10 was pushed at `e69675be`, and commit 11 retired the diff --git a/docs/wiki/log.md b/docs/wiki/log.md index 43346bcf..b06fb2f7 100644 --- a/docs/wiki/log.md +++ b/docs/wiki/log.md @@ -2346,3 +2346,40 @@ Updated the CI affected-validation LLD to describe published runner-family artif profile may proceed tests-first with wholly new coordinates and identities, one package-mutation attempt at a time, and mandatory reconciliation for ambiguity. Normal Live activation and `live_enabled: true` remain excluded. + +## [2026-08-28] query | Prepare destination acceptance retry 4 + +- Added the closed retry-4 Adapter and Governance profile using base `.13`, + absent/exact `.13`, identical-race `.14`, differing-race `.15`, and + lost-response `.16`, with tags `wdv3-acceptance-13` through + `wdv3-acceptance-16`. +- Added the temporary retry-4 workflow and fixed Environment identity with the + 40-zero target sentinel and confirmation + `I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4`. Validation fails before + Environment review or either package-write probe while the sentinel remains. +- Recorded 3,836 passing v3 tests, local HK gates, and multi-reviewer review + closure after independent TP/FP adjudication and repair. +- Kept the phase preparation-only: no Environment, acceptance dispatch, + package or tag mutation, or acceptance ref was created. After preparation + merges without bypass, external state must be revalidated before Environment + creation and a separate finalization PR pins the reviewed target to the + preparation merge SHA. +- Preserved all three historical attempts as unsuccessful, `.1`-`.12` as + consumed, `live_enabled: false`, and normal Live activation as unauthorized. + +## [2026-08-28] query | Close retry-4 rejected-dispatch evidence gap + +- Added + `test_retry_4_terminal_program_preserves_fixed_identity_after_rejected_dispatch` + to execute the temporary workflow's exact terminal Python after fixed-input + validation rejects deliberately wrong dispatch values. +- Proved that canonical terminal evidence remains bound to the fixed zero + target, `.13` package coordinate, retry-4 confirmation digest, workflow + identity, failed validation, skipped downstream jobs, and incomplete + mutation classification. +- Isolated the subprocess from ambient credentials, proxies, `PYTHONPATH`, and + user startup state with Python isolated mode, an isolated home, and a minimal + allowlisted environment. +- Recorded 3,837 passing v3 tests and a successful unstaged HK gate. The + follow-up remains preparation-only: no Environment, acceptance ref, + workflow dispatch, package, tag, or Live mutation occurred. diff --git a/docs/wiki/overview.md b/docs/wiki/overview.md index 12372d4b..26604741 100644 --- a/docs/wiki/overview.md +++ b/docs/wiki/overview.md @@ -334,8 +334,20 @@ have changed. as `bf174897`. Diagnostics remain observability only and cannot establish execution, mutation completion, or Governance acceptance. No acceptance invocation followed the repair before this documentation update. -8. Continue with a tests-first fourth profile using wholly new coordinates and - identities. Subsequent explicit user authorization covers the bounded +8. Preparation PR #608 now contains the tests-first fourth profile without + operating it: base `.13` binds absent/exact `.13`, identical-race `.14`, + differing-race `.15`, and lost-response `.16`; the temporary retry-4 + workflow and Environment identity are fixed; and the target remains exactly + 40 zeroes. Validation therefore stops before protected Environment review or + either package-write probe. No Environment, acceptance dispatch, package or + tag mutation, or acceptance ref was created. PR #608 is the preparation + carrier; no later phase may begin unless it has merged without bypass. Once + that prerequisite is satisfied, revalidate external state, create the fresh + protected Environment, and use a separate finalization PR to pin the + reviewed target to the preparation merge SHA. The finalization PR must also + merge without bypass and a fresh exact preflight must still pass before any + acceptance ref is created or the single `run_attempt == 1` attempt is + dispatched. Subsequent explicit user authorization covers the bounded acceptance-only repair/retry loop through genuine success, reconciliation, cleanup, and closure. It does not authorize normal Live activation or `live_enabled: true`. diff --git a/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/adapters/github_packages.py b/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/adapters/github_packages.py index 7af8d67a..06bc4070 100644 --- a/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/adapters/github_packages.py +++ b/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/adapters/github_packages.py @@ -115,6 +115,20 @@ ("differing-race", "0.0.0-wdv3-acceptance.11", "wdv3-acceptance-11"), ("lost-response", "0.0.0-wdv3-acceptance.12", "wdv3-acceptance-12"), ) +RETRY_4_ACCEPTANCE_PACKAGE_COORDINATE = ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13" +) +RETRY_4_ACCEPTANCE_SCENARIO_SPECS = ( + ( + "absent-create-readback", + "0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + ("exact", "0.0.0-wdv3-acceptance.13", "wdv3-acceptance-13"), + ("identical-race", "0.0.0-wdv3-acceptance.14", "wdv3-acceptance-14"), + ("differing-race", "0.0.0-wdv3-acceptance.15", "wdv3-acceptance-15"), + ("lost-response", "0.0.0-wdv3-acceptance.16", "wdv3-acceptance-16"), +) _ACCEPTANCE_SUITE_PROFILES = ( (ACCEPTANCE_PACKAGE_COORDINATE, ACCEPTANCE_SCENARIO_SPECS), ( @@ -125,6 +139,10 @@ RETRY_3_ACCEPTANCE_PACKAGE_COORDINATE, RETRY_3_ACCEPTANCE_SCENARIO_SPECS, ), + ( + RETRY_4_ACCEPTANCE_PACKAGE_COORDINATE, + RETRY_4_ACCEPTANCE_SCENARIO_SPECS, + ), ) ACCEPTANCE_COORDINATES = { scenario: f"{ACCEPTANCE_PACKAGE_NAME}@{version}" diff --git a/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/records/governance.py b/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/records/governance.py index 33846c13..79deaa86 100644 --- a/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/records/governance.py +++ b/src/public/lib/three-workflow-delivery-v3/src/three_workflow_delivery_v3/records/governance.py @@ -142,6 +142,37 @@ "wdv3-acceptance-12", ), } +GOVERNANCE_RETRY_4_ACCEPTANCE_WORKFLOW_PATH = ( + ".github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" +) +GOVERNANCE_RETRY_4_ACCEPTANCE_PACKAGE_COORDINATE = ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13" +) +GOVERNANCE_RETRY_4_ACCEPTANCE_ENVIRONMENT = ( + "workflow-delivery-v3-buddy-smoke-acceptance-retry-4" +) +GOVERNANCE_RETRY_4_ACCEPTANCE_SCENARIO_COORDINATES = { + "absent-create-readback": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + "exact": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + "identical-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.14", + "wdv3-acceptance-14", + ), + "differing-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.15", + "wdv3-acceptance-15", + ), + "lost-response": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.16", + "wdv3-acceptance-16", + ), +} @dataclass(frozen=True, slots=True) @@ -221,6 +252,26 @@ def coordinates(self) -> dict[str, tuple[str, str]]: ) ), ), + _GovernanceAcceptanceProfile( + package_coordinate=GOVERNANCE_RETRY_4_ACCEPTANCE_PACKAGE_COORDINATE, + workflow_path=GOVERNANCE_RETRY_4_ACCEPTANCE_WORKFLOW_PATH, + environment=GOVERNANCE_RETRY_4_ACCEPTANCE_ENVIRONMENT, + target_sha="0000000000000000000000000000000000000000", + confirmation_digest=( + "sha256:" + "b6f94d3c13c98b0714404959dd878230f8302ee849038a536f5a18cc3a85c7ec" + ), + scenario_coordinates=tuple( + ( + scenario, + coordinate, + tag, + ) + for scenario, (coordinate, tag) in ( + GOVERNANCE_RETRY_4_ACCEPTANCE_SCENARIO_COORDINATES.items() + ) + ), + ), ) diff --git a/src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py b/src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py index 1c72fe93..c1265217 100644 --- a/src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py +++ b/src/public/lib/three-workflow-delivery-v3/tests/adapters/test_commit10_acceptance_probes.py @@ -239,7 +239,7 @@ def test_retry_2_suite_resolves_only_the_reviewed_coordinate_block() -> None: } with pytest.raises(ValueError, match="not a reviewed fixed suite"): fixed_acceptance_coordinates( - "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13" + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.17" ) @@ -2526,8 +2526,8 @@ def test_acceptance_symbols_are_deliberately_public_when_adapter_exports_them() (ACCEPTANCE_COORDINATES["absent-create-readback"], "latest"), (ACCEPTANCE_COORDINATES["exact"], "wdv3-acceptance-9"), ( - "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", - "wdv3-acceptance-13", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.17", + "wdv3-acceptance-17", ), ], ) @@ -7389,3 +7389,548 @@ def publish(index: int) -> None: "exception-category": None, "request-correlation-digest": request_digest, } + + +_RETRY_4_ACCEPTANCE_PACKAGE_NAME = "@hcoona/hcoona-release-smoke-npm" +_RETRY_4_ACCEPTANCE_BASE_COORDINATE = ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13" +) +_RETRY_4_UNREGISTERED_BASE_COORDINATE = ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.17" +) +_RETRY_4_ACCEPTANCE_BINDINGS = ( + ( + "absent-create-readback", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + ( + "exact", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + ( + "identical-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.14", + "wdv3-acceptance-14", + ), + ( + "differing-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.15", + "wdv3-acceptance-15", + ), + ( + "lost-response", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.16", + "wdv3-acceptance-16", + ), +) + + +def _require_retry_4_adapter_profile() -> Any: + from three_workflow_delivery_v3.adapters import ( # noqa: PLC0415 + github_packages as module, + ) + + registered_base_coordinates = tuple( + base_coordinate + for base_coordinate, _scenario_specs in module._ACCEPTANCE_SUITE_PROFILES + ) + assert _RETRY_4_ACCEPTANCE_BASE_COORDINATE in registered_base_coordinates, ( + "E-ADAPTER-PROFILE-ABSENT: the reviewed retry-4 base coordinate is " + f"not registered; observed {registered_base_coordinates!r}" + ) + return module + + +def test_retry_4_adapter_profiles_have_stable_historical_order_and_unique_base_coordinates() -> ( + None +): + module = _require_retry_4_adapter_profile() + + base_coordinates = tuple( + base_coordinate + for base_coordinate, _scenario_specs in module._ACCEPTANCE_SUITE_PROFILES + ) + + assert base_coordinates == ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.1", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + ) + assert len(base_coordinates) == 4 + assert len(set(base_coordinates)) == 4 + + +def test_retry_4_adapter_profiles_preserve_scenario_order_and_qualified_identity_uniqueness() -> ( + None +): + module = _require_retry_4_adapter_profile() + profiles = module._ACCEPTANCE_SUITE_PROFILES + expected_scenario_order = ( + "absent-create-readback", + "exact", + "identical-race", + "differing-race", + "lost-response", + ) + + scenario_orders = tuple( + tuple(scenario for scenario, _version, _tag in scenario_specs) + for _base_coordinate, scenario_specs in profiles + ) + qualified_identities = tuple( + ( + base_coordinate, + scenario, + f"{_RETRY_4_ACCEPTANCE_PACKAGE_NAME}@{version}", + tag, + ) + for base_coordinate, scenario_specs in profiles + for scenario, version, tag in scenario_specs + ) + reuse_groups = [] + for _base_coordinate, scenario_specs in profiles: + scenarios_by_pair: dict[tuple[str, str], list[str]] = {} + for scenario, version, tag in scenario_specs: + pair = ( + f"{_RETRY_4_ACCEPTANCE_PACKAGE_NAME}@{version}", + tag, + ) + scenarios_by_pair.setdefault(pair, []).append(scenario) + reuse_groups.append( + tuple(tuple(scenarios) for scenarios in scenarios_by_pair.values()) + ) + + assert scenario_orders == (expected_scenario_order,) * 4 + assert len(qualified_identities) == 20 + assert len(set(qualified_identities)) == 20 + assert ( + tuple(reuse_groups) + == ( + ( + ("absent-create-readback", "exact"), + ("identical-race",), + ("differing-race",), + ("lost-response",), + ), + ) + * 4 + ) + + +def test_retry_4_adapter_coordinate_tag_pairs_are_exact_and_globally_unique() -> ( + None +): + module = _require_retry_4_adapter_profile() + expected_pair_blocks = ( + frozenset( + { + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.1", + "wdv3-acceptance-1", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.2", + "wdv3-acceptance-2", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.3", + "wdv3-acceptance-3", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.4", + "wdv3-acceptance-4", + ), + } + ), + frozenset( + { + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "wdv3-acceptance-5", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.6", + "wdv3-acceptance-6", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.7", + "wdv3-acceptance-7", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.8", + "wdv3-acceptance-8", + ), + } + ), + frozenset( + { + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "wdv3-acceptance-9", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.10", + "wdv3-acceptance-10", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.11", + "wdv3-acceptance-11", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.12", + "wdv3-acceptance-12", + ), + } + ), + frozenset( + { + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.14", + "wdv3-acceptance-14", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.15", + "wdv3-acceptance-15", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.16", + "wdv3-acceptance-16", + ), + } + ), + ) + actual_pair_blocks = tuple( + frozenset( + ( + f"{_RETRY_4_ACCEPTANCE_PACKAGE_NAME}@{version}", + tag, + ) + for _scenario, version, tag in scenario_specs + ) + for _base_coordinate, scenario_specs in module._ACCEPTANCE_SUITE_PROFILES + ) + all_pairs = frozenset( + pair for block in actual_pair_blocks for pair in block + ) + retry_4_specs = next( + scenario_specs + for base_coordinate, scenario_specs in module._ACCEPTANCE_SUITE_PROFILES + if base_coordinate == _RETRY_4_ACCEPTANCE_BASE_COORDINATE + ) + retry_4_bindings = tuple( + ( + scenario, + f"{_RETRY_4_ACCEPTANCE_PACKAGE_NAME}@{version}", + tag, + ) + for scenario, version, tag in retry_4_specs + ) + + assert actual_pair_blocks == expected_pair_blocks + assert tuple(len(block) for block in actual_pair_blocks) == (4, 4, 4, 4) + assert len(all_pairs) == 16 + assert sum(len(block) for block in actual_pair_blocks) == len(all_pairs) + assert all_pairs == module._ACCEPTANCE_COORDINATE_TAG_PAIRS + assert retry_4_bindings == _RETRY_4_ACCEPTANCE_BINDINGS + + +def test_retry_4_fixed_acceptance_resolvers_return_exact_scenarios_and_coordinates() -> ( + None +): + _require_retry_4_adapter_profile() + expected_specs = tuple( + ( + scenario, + coordinate.rsplit("@", 1)[1], + tag, + ) + for scenario, coordinate, tag in _RETRY_4_ACCEPTANCE_BINDINGS + ) + expected_coordinates = tuple( + (scenario, coordinate) + for scenario, coordinate, _tag in _RETRY_4_ACCEPTANCE_BINDINGS + ) + + assert ( + fixed_acceptance_scenario_specs(_RETRY_4_ACCEPTANCE_BASE_COORDINATE) + == expected_specs + ) + assert ( + tuple( + fixed_acceptance_coordinates( + _RETRY_4_ACCEPTANCE_BASE_COORDINATE + ).items() + ) + == expected_coordinates + ) + with pytest.raises(ValueError, match="not a reviewed fixed suite"): + fixed_acceptance_scenario_specs(_RETRY_4_UNREGISTERED_BASE_COORDINATE) + with pytest.raises(ValueError, match="not a reviewed fixed suite"): + fixed_acceptance_coordinates(_RETRY_4_UNREGISTERED_BASE_COORDINATE) + + +def test_retry_4_npm_runner_invokes_all_five_exact_coordinates() -> None: + _require_retry_4_adapter_profile() + runner = cli_module._AcceptanceNpmRunner( + Path(".retry-4.npmrc"), + contender_tarballs={}, + base_package_coordinate=_RETRY_4_ACCEPTANCE_BASE_COORDINATE, + ) + + assert tuple(runner._coordinates.items()) == tuple( + (scenario, coordinate) + for scenario, coordinate, _tag in _RETRY_4_ACCEPTANCE_BINDINGS + ) + assert len(runner._coordinates) == 5 + assert ( + runner._coordinates["absent-create-readback"] + == runner._coordinates["exact"] + == _RETRY_4_ACCEPTANCE_BASE_COORDINATE + ) + + +def test_retry_4_fixed_acceptance_suite_routes_exact_bindings_through_controlled_fakes( + tmp_path: Path, +) -> None: + _require_retry_4_adapter_profile() + coordinates = { + scenario: coordinate + for scenario, coordinate, _tag in _RETRY_4_ACCEPTANCE_BINDINGS + } + tags = { + scenario: tag + for scenario, _coordinate, tag in _RETRY_4_ACCEPTANCE_BINDINGS + } + tarballs = {} + for scenario, _coordinate, _tag in _RETRY_4_ACCEPTANCE_BINDINGS: + tarball = tmp_path / f"retry-4-{scenario}.tgz" + tarball.write_bytes(f"retry-4-{scenario}".encode()) + tarballs[scenario] = tarball + + def exact_observation(scenario: str) -> dict[str, Any]: + return { + "state": "exact", + "version": coordinates[scenario].rsplit("@", 1)[1], + "tag": tags[scenario], + "content-sha512": ( + "sha512:" + + hashlib.sha512(tarballs[scenario].read_bytes()).hexdigest() + ), + "response-identity-digest": RESPONSE_B, + } + + transport = RecordingTransport( + [ + _absent(), + exact_observation("absent-create-readback"), + exact_observation("exact"), + _absent(), + exact_observation("identical-race"), + _absent(), + exact_observation("differing-race"), + _absent(), + exact_observation("lost-response"), + ] + ) + runner = ControlledRunner() + + absent_result = run_fixed_acceptance_suite( + suite="absent-create-readback", + tarballs={"absent-create-readback": tarballs["absent-create-readback"]}, + transport=transport, + runner=runner, + timeout_seconds=TIMEOUT_SECONDS, + max_response_bytes=MAX_RESPONSE_BYTES, + max_output_bytes=MAX_OUTPUT_BYTES, + base_package_coordinate=_RETRY_4_ACCEPTANCE_BASE_COORDINATE, + ) + conflict_result = run_fixed_acceptance_suite( + suite="exact-and-conflict", + tarballs={ + scenario: tarballs[scenario] + for scenario in ( + "exact", + "identical-race", + "differing-race", + "lost-response", + ) + }, + transport=transport, + runner=runner, + timeout_seconds=TIMEOUT_SECONDS, + max_response_bytes=MAX_RESPONSE_BYTES, + max_output_bytes=MAX_OUTPUT_BYTES, + base_package_coordinate=_RETRY_4_ACCEPTANCE_BASE_COORDINATE, + ) + + routed_results = (*absent_result.scenarios, *conflict_result.scenarios) + assert ( + tuple( + (result.scenario, result.package_coordinate, result.tag) + for result in routed_results + ) + == _RETRY_4_ACCEPTANCE_BINDINGS + ) + assert tuple((call[0], call[1]) for call in transport.calls) == ( + (coordinates["absent-create-readback"], tags["absent-create-readback"]), + (coordinates["absent-create-readback"], tags["absent-create-readback"]), + (coordinates["exact"], tags["exact"]), + (coordinates["identical-race"], tags["identical-race"]), + (coordinates["identical-race"], tags["identical-race"]), + (coordinates["differing-race"], tags["differing-race"]), + (coordinates["differing-race"], tags["differing-race"]), + (coordinates["lost-response"], tags["lost-response"]), + (coordinates["lost-response"], tags["lost-response"]), + ) + assert all( + 0 < timeout_seconds <= TIMEOUT_SECONDS + and max_response_bytes == MAX_RESPONSE_BYTES + for _coordinate, _tag, timeout_seconds, max_response_bytes in transport.calls + ) + assert tuple( + ( + scenario, + argv[2], + argv[argv.index("--tag") + 1], + ) + for scenario, argv, _env in runner.calls + ) == ( + ( + "absent-create-readback", + str(tarballs["absent-create-readback"]), + "wdv3-acceptance-13", + ), + ( + "identical-race", + str(tarballs["identical-race"]), + "wdv3-acceptance-14", + ), + ( + "differing-race", + str(tarballs["differing-race"]), + "wdv3-acceptance-15", + ), + ( + "lost-response", + str(tarballs["lost-response"]), + "wdv3-acceptance-16", + ), + ) + assert ( + absent_result.suite, + absent_result.scenario_inventory, + absent_result.mutation_classification, + absent_result.result, + ) == ( + "absent-create-readback", + ("absent-create-readback",), + "incomplete", + "incomplete", + ) + assert ( + conflict_result.suite, + conflict_result.scenario_inventory, + conflict_result.mutation_classification, + conflict_result.result, + ) == ( + "exact-and-conflict", + ("exact", "identical-race", "differing-race", "lost-response"), + "unknown", + "unknown", + ) + assert tuple(result.result for result in routed_results) == ( + "created-without-request-proof", + "exact-no-mutation", + "created", + "differing-race-winner-not-proven", + "lost-response", + ) + + +def test_retry_4_validated_proof_accepts_exact_coordinate_tag_bindings() -> ( + None +): + _require_retry_4_adapter_profile() + proofs = tuple( + AcceptanceRequestProof.from_validated_exchange( + raw_request=f"retry-4-request-{index}-{scenario}".encode(), + tarball=f"retry-4-tarball-{index}-{scenario}".encode(), + package_coordinate=coordinate, + tag=tag, + upstream_status=201, + selected_headers={ + "Content-Type": "application/json", + "ETag": f'"retry-4-{index}"', + }, + response_body=f'{{"retry":4,"scenario":"{scenario}"}}'.encode(), + ) + for index, (scenario, coordinate, tag) in enumerate( + _RETRY_4_ACCEPTANCE_BINDINGS, + start=1, + ) + ) + + assert tuple( + (proof.package_coordinate, proof.tag) for proof in proofs + ) == tuple( + (coordinate, tag) + for _scenario, coordinate, tag in _RETRY_4_ACCEPTANCE_BINDINGS + ) + assert tuple(proof.upstream_status for proof in proofs) == (201,) * 5 + assert tuple(proof.selected_headers for proof in proofs) == tuple( + ( + ("content-type", "application/json"), + ("etag", f'"retry-4-{index}"'), + ) + for index in range(1, 6) + ) + assert len({proof.request_digest for proof in proofs}) == 5 + assert len({proof.response_identity_digest for proof in proofs}) == 5 + assert {proof.to_document()["schema"] for proof in proofs} == { + "workflow-delivery/v3/validated-acceptance-request-proof" + } + + +def test_retry_4_validated_proof_rejects_historical_substitutions_in_both_directions() -> ( + None +): + _require_retry_4_adapter_profile() + substitutions = ( + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-9", + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "wdv3-acceptance-13", + ), + ) + observed_messages = [] + + for index, (coordinate, tag) in enumerate(substitutions, start=1): + with pytest.raises( + ValueError, + match="coordinate or tag is not fixed", + ) as raised: + AcceptanceRequestProof.from_validated_exchange( + raw_request=f"retry-4-substitution-request-{index}".encode(), + tarball=f"retry-4-substitution-tarball-{index}".encode(), + package_coordinate=coordinate, + tag=tag, + upstream_status=201, + selected_headers={"Content-Type": "application/json"}, + response_body=b'{"ok":true}', + ) + observed_messages.append(str(raised.value)) + + assert observed_messages == [ + "validated request coordinate or tag is not fixed", + "validated request coordinate or tag is not fixed", + ] diff --git a/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py b/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py index 772de304..242803df 100644 --- a/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py +++ b/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_buddy_workflows.py @@ -4562,25 +4562,64 @@ def test_live_eligibility_admission_receives_current_intent_and_model_before_his assert option in capability -def test_temporary_acceptance_workflows_are_absent_with_disabled_normal_buddy() -> ( +def test_retry_4_is_the_only_required_temporary_acceptance_workflow_during_preparation() -> ( None ): - original = ( - REPO_ROOT - / ".github/workflows/workflow-delivery-v3-buddy-smoke-acceptance.yml" + workflows = REPO_ROOT / ".github/workflows" + retry_4 = ( + workflows / "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" ) - retry_2 = ( - REPO_ROOT - / ".github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-2.yml" + temporary_workflows = tuple( + sorted( + { + *workflows.glob( + "workflow-delivery-v3-buddy-smoke-acceptance*.yml" + ), + *workflows.glob( + "workflow-delivery-v3-buddy-smoke-acceptance*.yaml" + ), + } + ) ) - retry_3 = ( - REPO_ROOT - / ".github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-3.yml" + + assert temporary_workflows == (retry_4,), ( + "E-WORKFLOW-ABSENT: required retry-4 workflow is absent at " + ".github/workflows/" + "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" + if retry_4 not in temporary_workflows + else ( + "retry-4 must be the only temporary acceptance workflow during " + f"preparation: {temporary_workflows!r}" + ) ) - assert not original.exists() - assert not retry_2.exists() - assert not retry_3.exists() - if GOVERNANCE.exists(): - governance = json.loads(GOVERNANCE.read_text(encoding="utf-8")) - assert governance["live_enabled"] is False + +def test_retry_4_preparation_keeps_normal_buddy_disabled_and_live_enabled_false() -> ( + None +): + caller = _document(CALLER) + callee = _document(CALLEE) + caller_document = cast("dict[object, Any]", caller) + callee_document = cast("dict[object, Any]", callee) + caller_triggers = caller_document.get( + "on", + caller_document.get(True), + ) + callee_triggers = callee_document.get( + "on", + callee_document.get(True), + ) + raw = CALLER.read_text(encoding="utf-8") + CALLEE.read_text( + encoding="utf-8" + ) + + assert caller_triggers == {"workflow_dispatch": None} + assert isinstance(callee_triggers, dict) + assert set(callee_triggers) == {"workflow_call"} + assert "schedule:" not in raw + assert "push:" not in raw + assert "live_enabled: true" not in raw + assert "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" not in raw + assert GOVERNANCE.is_file() + governance = json.loads(GOVERNANCE.read_text(encoding="utf-8")) + assert governance["live_enabled"] is False diff --git a/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py b/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py new file mode 100644 index 00000000..75280720 --- /dev/null +++ b/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit10_acceptance_retry_4_workflow.py @@ -0,0 +1,1062 @@ +"""Static contract for the temporary retry-4 acceptance workflow.""" + +# ruff: noqa: D103, E501, PLR2004, S603 + +from __future__ import annotations + +import ast +import hashlib +import os +import re +import subprocess +from pathlib import Path +from typing import Any, cast + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[6] +WORKFLOW_RELATIVE_PATH = ( + ".github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" +) +WORKFLOW_PATH = REPO_ROOT / WORKFLOW_RELATIVE_PATH +WORKFLOW_STEM = "workflow-delivery-v3-buddy-smoke-acceptance-retry-4" +ENVIRONMENT = "workflow-delivery-v3-buddy-smoke-acceptance-retry-4" + +ZERO_SHA = "0" * 40 +TEST_ONLY_NONZERO_TARGET_SHA = "d" * 40 +PACKAGE_NAME = "@hcoona/hcoona-release-smoke-npm" +BASE_COORDINATE = "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13" +CONFIRMATION = "I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4" +CONFIRMATION_DIGEST = ( + "sha256:b6f94d3c13c98b0714404959dd878230f8302ee849038a536f5a18cc3a85c7ec" +) + +JOB_ORDER = ( + "validate-fixed-inputs", + "acceptance-review", + "probe-absent-create-readback", + "probe-exact-and-conflict", + "capture-governance-evidence", +) +PROBE_JOBS = ( + "probe-absent-create-readback", + "probe-exact-and-conflict", +) + +CHECKOUT = "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" +SETUP_UV = "astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d" +SETUP_NODE = "actions/setup-node@820762786026740c76f36085b0efc47a31fe5020" +UPLOAD = "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" +ACTION_PINS = {CHECKOUT, SETUP_UV, SETUP_NODE, UPLOAD} + + +def _load_workflow() -> tuple[dict[str, Any], str]: + """Load the workflow only after a test body has started.""" + assert WORKFLOW_PATH.is_file(), ( + "E-WORKFLOW-ABSENT: required retry-4 workflow is absent at " + f"{WORKFLOW_RELATIVE_PATH}" + ) + raw = WORKFLOW_PATH.read_text(encoding="utf-8") + value = yaml.safe_load(raw) + assert isinstance(value, dict), "retry-4 workflow must be a YAML mapping" + return cast("dict[str, Any]", value), raw + + +def _triggers(document: dict[str, Any]) -> dict[str, Any]: + value = document.get("on") + if value is None: + value = cast("dict[object, Any]", document).get(True) + assert isinstance(value, dict) + return cast("dict[str, Any]", value) + + +def _jobs(document: dict[str, Any]) -> dict[str, dict[str, Any]]: + value = document.get("jobs") + assert isinstance(value, dict) + assert all(isinstance(job, dict) for job in value.values()) + return cast("dict[str, dict[str, Any]]", value) + + +def _steps(job: dict[str, Any]) -> list[dict[str, Any]]: + value = job.get("steps") + assert isinstance(value, list) + assert all(isinstance(step, dict) for step in value) + return cast("list[dict[str, Any]]", value) + + +def _step(job: dict[str, Any], name: str) -> dict[str, Any]: + matches = [step for step in _steps(job) if step.get("name") == name] + assert len(matches) == 1, f"{name!r} must occur exactly once" + return matches[0] + + +def _needs(job: dict[str, Any]) -> tuple[str, ...]: + value = job.get("needs", ()) + if isinstance(value, str): + return (value,) + assert isinstance(value, (list, tuple)) + assert all(isinstance(name, str) for name in value) + return tuple(value) + + +def _transitive_needs( + jobs: dict[str, dict[str, Any]], + job_name: str, +) -> set[str]: + dependencies: set[str] = set() + pending = list(_needs(jobs[job_name])) + while pending: + dependency = pending.pop() + assert dependency in jobs + if dependency in dependencies: + continue + dependencies.add(dependency) + pending.extend(_needs(jobs[dependency])) + return dependencies + + +def _validation_script(document: dict[str, Any]) -> str: + validation = _step( + _jobs(document)["validate-fixed-inputs"], + "Fail closed before review or mutation", + ) + run = validation.get("run") + assert isinstance(run, str) + return run + + +def _guard_conditions(script: str) -> list[str]: + return re.findall(r"if \[\[ (.+) \]\]; then", script) + + +def _run_fixed_input_guard( + document: dict[str, Any], + *, + target_sha: str, + overrides: dict[str, str] | None = None, +) -> subprocess.CompletedProcess[str]: + environment = { + **os.environ, + "INPUT_TARGET_SHA": target_sha, + "INPUT_PACKAGE_COORDINATE": BASE_COORDINATE, + "INPUT_CONFIRM": CONFIRMATION, + "WDV3_ACCEPTANCE_TARGET_SHA": target_sha, + "WDV3_ACCEPTANCE_PACKAGE_COORDINATE": BASE_COORDINATE, + "WDV3_ACCEPTANCE_CONFIRMATION": CONFIRMATION, + "WDV3_ACCEPTANCE_REF": "refs/heads/main", + "GITHUB_REF": "refs/heads/main", + "GITHUB_RUN_ATTEMPT": "1", + } + environment.update(overrides or {}) + return subprocess.run( + ("/usr/bin/bash", "-c", _validation_script(document)), + check=False, + capture_output=True, + text=True, + env=environment, + ) + + +def _terminal_python(document: dict[str, Any]) -> str: + terminal = _step( + _jobs(document)["capture-governance-evidence"], + "Form and admit terminal Governance evidence", + ) + run = terminal.get("run") + assert isinstance(run, str) + marker = "python - <<'PY'\n" + assert run.count(marker) == 1 + script, terminator = run.split(marker, 1)[1].rsplit("\nPY", 1) + assert not terminator.strip() + return script + + +def _assigned_value(tree: ast.Module, name: str) -> ast.expr: + matches = [ + node.value + for node in tree.body + if isinstance(node, ast.Assign) + and any( + isinstance(target, ast.Name) and target.id == name + for target in node.targets + ) + ] + assert len(matches) == 1, f"{name!r} must be assigned exactly once" + return matches[0] + + +def _dict_fields(node: ast.expr) -> dict[str, ast.expr]: + assert isinstance(node, ast.Dict) + fields: dict[str, ast.expr] = {} + for key, value in zip(node.keys, node.values, strict=True): + assert isinstance(key, ast.Constant) + assert isinstance(key.value, str) + fields[key.value] = value + return fields + + +def test_retry_4_workflow_uses_exact_temporary_path_stem_and_environment_identity() -> ( + None +): + document, _raw = _load_workflow() + jobs = _jobs(document) + + assert WORKFLOW_PATH.relative_to(REPO_ROOT).as_posix() == ( + ".github/workflows/" + "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" + ) + assert WORKFLOW_PATH.name == ( + "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" + ) + assert WORKFLOW_PATH.stem == WORKFLOW_STEM + assert ENVIRONMENT == WORKFLOW_STEM + assert document["name"] == ( + "Workflow Delivery v3 Buddy smoke destination acceptance retry 4" + ) + assert jobs["acceptance-review"]["environment"] == ENVIRONMENT + assert [name for name, job in jobs.items() if "environment" in job] == [ + "acceptance-review" + ] + + +def test_retry_4_workflow_declares_exact_five_jobs_in_order() -> None: + document, _raw = _load_workflow() + jobs = _jobs(document) + + assert tuple(jobs) == JOB_ORDER + assert {name: _needs(job) for name, job in jobs.items()} == { + "validate-fixed-inputs": (), + "acceptance-review": ("validate-fixed-inputs",), + "probe-absent-create-readback": ("acceptance-review",), + "probe-exact-and-conflict": ("probe-absent-create-readback",), + "capture-governance-evidence": ( + "validate-fixed-inputs", + "acceptance-review", + "probe-absent-create-readback", + "probe-exact-and-conflict", + ), + } + assert {name: _transitive_needs(jobs, name) for name in JOB_ORDER} == { + "validate-fixed-inputs": set(), + "acceptance-review": {"validate-fixed-inputs"}, + "probe-absent-create-readback": { + "validate-fixed-inputs", + "acceptance-review", + }, + "probe-exact-and-conflict": { + "validate-fixed-inputs", + "acceptance-review", + "probe-absent-create-readback", + }, + "capture-governance-evidence": set(JOB_ORDER[:-1]), + } + + +def test_retry_4_workflow_applies_first_attempt_guards_and_terminal_always_capture() -> ( + None +): + document, _raw = _load_workflow() + jobs = _jobs(document) + + assert {name: job.get("if") for name, job in jobs.items()} == { + "validate-fixed-inputs": "${{ github.run_attempt == 1 }}", + "acceptance-review": "${{ github.run_attempt == 1 }}", + "probe-absent-create-readback": ("${{ github.run_attempt == 1 }}"), + "probe-exact-and-conflict": ( + "${{ github.run_attempt == 1 && " + "needs.probe-absent-create-readback.result == 'success' }}" + ), + "capture-governance-evidence": ( + "${{ always() && github.run_attempt == 1 }}" + ), + } + assert all( + "github.run_attempt == 1" in cast("str", jobs[name]["if"]) + for name in JOB_ORDER + ) + assert jobs["capture-governance-evidence"]["if"].startswith( + "${{ always() && " + ) + for job_name in PROBE_JOBS: + assert [step.get("if") for step in _steps(jobs[job_name])[-3:]] == [ + "${{ always() }}", + "${{ always() }}", + "${{ always() }}", + ] + + +def test_retry_4_workflow_scopes_environment_and_packages_write_permissions_to_exact_jobs() -> ( + None +): + document, _raw = _load_workflow() + jobs = _jobs(document) + + assert document["permissions"] == {} + assert {name: job.get("permissions") for name, job in jobs.items()} == { + "validate-fixed-inputs": {"contents": "read"}, + "acceptance-review": {}, + "probe-absent-create-readback": { + "contents": "read", + "packages": "write", + }, + "probe-exact-and-conflict": { + "contents": "read", + "packages": "write", + }, + "capture-governance-evidence": {"contents": "read"}, + } + assert { + name + for name, job in jobs.items() + if job.get("permissions", {}).get("packages") == "write" + } == set(PROBE_JOBS) + assert { + name: job["environment"] + for name, job in jobs.items() + if "environment" in job + } == {"acceptance-review": ENVIRONMENT} + assert all( + permission in {"read", "write"} + for job in jobs.values() + for permission in job.get("permissions", {}).values() + ) + + +def test_retry_4_workflow_zero_target_stops_before_review_and_write_capable_probes() -> ( + None +): + document, _raw = _load_workflow() + jobs = _jobs(document) + inputs = _triggers(document)["workflow_dispatch"]["inputs"] + validation = jobs["validate-fixed-inputs"] + guard = _step(validation, "Fail closed before review or mutation") + script = _validation_script(document) + + assert ZERO_SHA == "0000000000000000000000000000000000000000" + assert len(ZERO_SHA) == 40 + assert ZERO_SHA.isascii() + assert set(ZERO_SHA) == {"0"} + assert inputs["target_sha"]["default"] == ZERO_SHA + assert document["env"]["WDV3_ACCEPTANCE_TARGET_SHA"] == ZERO_SHA + assert validation.get("continue-on-error", False) is False + assert guard.get("continue-on-error", False) is False + assert guard["env"] == { + "INPUT_TARGET_SHA": "${{ inputs.target_sha }}", + "INPUT_PACKAGE_COORDINATE": "${{ inputs.package_coordinate }}", + "INPUT_CONFIRM": "${{ inputs.confirm }}", + } + assert _guard_conditions(script) == [ + '"${INPUT_TARGET_SHA}" != "${WDV3_ACCEPTANCE_TARGET_SHA}"', + f'"${{WDV3_ACCEPTANCE_TARGET_SHA}}" == "{ZERO_SHA}"', + f'"${{INPUT_TARGET_SHA}}" == "{ZERO_SHA}"', + ( + '"${INPUT_PACKAGE_COORDINATE}" != ' + '"${WDV3_ACCEPTANCE_PACKAGE_COORDINATE}"' + ), + '"${INPUT_CONFIRM}" != "${WDV3_ACCEPTANCE_CONFIRMATION}"', + '"${GITHUB_REF}" != "${WDV3_ACCEPTANCE_REF}"', + '"${GITHUB_RUN_ATTEMPT}" != "1"', + ] + assert [step["name"] for step in _steps(jobs["validate-fixed-inputs"])] == [ + "Fail closed before review or mutation" + ] + + result = _run_fixed_input_guard(document, target_sha=ZERO_SHA) + + assert (result.returncode, result.stdout, result.stderr) == (1, "", "") + for downstream in ("acceptance-review", *PROBE_JOBS): + assert "validate-fixed-inputs" in _transitive_needs(jobs, downstream) + assert all( + jobs[name]["permissions"]["packages"] == "write" for name in PROBE_JOBS + ) + assert "packages" not in jobs["capture-governance-evidence"]["permissions"] + + +def test_retry_4_workflow_test_only_nonzero_placeholder_satisfies_finalized_guard_shape() -> ( + None +): + document, raw = _load_workflow() + inputs = _triggers(document)["workflow_dispatch"]["inputs"] + + assert TEST_ONLY_NONZERO_TARGET_SHA == "d" * 40 + assert re.fullmatch(r"[0-9a-f]{40}", TEST_ONLY_NONZERO_TARGET_SHA) + assert TEST_ONLY_NONZERO_TARGET_SHA != ZERO_SHA + assert TEST_ONLY_NONZERO_TARGET_SHA not in raw + assert inputs["target_sha"]["default"] == ZERO_SHA + assert document["env"]["WDV3_ACCEPTANCE_TARGET_SHA"] == ZERO_SHA + + result = _run_fixed_input_guard( + document, + target_sha=TEST_ONLY_NONZERO_TARGET_SHA, + ) + + assert (result.returncode, result.stdout, result.stderr) == (0, "", "") + assert _guard_conditions(_validation_script(document))[1:3] == [ + f'"${{WDV3_ACCEPTANCE_TARGET_SHA}}" == "{ZERO_SHA}"', + f'"${{INPUT_TARGET_SHA}}" == "{ZERO_SHA}"', + ] + + +def test_retry_4_workflow_dispatch_identity_confirmation_digest_and_concurrency_are_exact() -> ( + None +): + document, raw = _load_workflow() + triggers = _triggers(document) + inputs = triggers["workflow_dispatch"]["inputs"] + + assert tuple(triggers) == ("workflow_dispatch",) + assert tuple(inputs) == ("target_sha", "package_coordinate", "confirm") + assert inputs == { + "target_sha": { + "description": "Reviewed protected-finalization target SHA", + "required": True, + "default": ZERO_SHA, + "type": "string", + }, + "package_coordinate": { + "description": "Fixed disposable GitHub Packages base coordinate", + "required": True, + "default": BASE_COORDINATE, + "type": "string", + }, + "confirm": { + "description": "Explicit disposable-probe confirmation", + "required": True, + "default": CONFIRMATION, + "type": "string", + }, + } + assert document["env"] == { + "WDV3_ACCEPTANCE_TARGET_SHA": ZERO_SHA, + "WDV3_ACCEPTANCE_PACKAGE_COORDINATE": BASE_COORDINATE, + "WDV3_ACCEPTANCE_CONFIRMATION": CONFIRMATION, + "WDV3_ACCEPTANCE_REF": "refs/heads/main", + "WDV3_PURPOSE": "destination-acceptance", + } + package_name, version = BASE_COORDINATE.rsplit("@", 1) + assert package_name == PACKAGE_NAME + assert version == "0.0.0-wdv3-acceptance.13" + assert raw.count(BASE_COORDINATE) == 3 + assert raw.count(CONFIRMATION) == 2 + assert ( + "sha256:" + hashlib.sha256(CONFIRMATION.encode()).hexdigest() + == CONFIRMATION_DIGEST + ) + assert document["concurrency"] == { + "group": ( + "hcoona-release-smoke-npm-workflow-delivery-v3-" + "buddy-smoke-acceptance-retry-4" + ), + "cancel-in-progress": False, + } + + +def test_retry_4_workflow_pins_current_actions_toolchains_checkout_and_probe_wiring() -> ( + None +): + document, _raw = _load_workflow() + jobs = _jobs(document) + uses_by_job = { + name: [ + cast("str", step["uses"]) for step in _steps(job) if "uses" in step + ] + for name, job in jobs.items() + } + uses = [use for job_uses in uses_by_job.values() for use in job_uses] + + assert uses_by_job == { + "validate-fixed-inputs": [], + "acceptance-review": [UPLOAD], + "probe-absent-create-readback": [ + CHECKOUT, + SETUP_UV, + SETUP_NODE, + UPLOAD, + ], + "probe-exact-and-conflict": [ + CHECKOUT, + SETUP_UV, + SETUP_NODE, + UPLOAD, + ], + "capture-governance-evidence": [CHECKOUT, SETUP_UV, UPLOAD], + } + assert set(uses) == ACTION_PINS + assert all(re.fullmatch(r"[^@\s]+@[0-9a-f]{40}", use) for use in uses) + + expected_outputs = { + "result": "${{ steps.classify.outputs.result }}", + "mutation-classification": ( + "${{ steps.classify.outputs.mutation-classification }}" + ), + "scenario-inventory": ( + "${{ steps.classify.outputs.scenario-inventory }}" + ), + "record-digest": "${{ steps.classify.outputs.record-digest }}", + "record-json": "${{ steps.classify.outputs.record-json }}", + "artifact-id": "${{ steps.upload.outputs.artifact-id }}", + "artifact-digest": "${{ steps.upload.outputs.artifact-digest }}", + } + expected_steps = { + "probe-absent-create-readback": ( + "Check out reviewed target", + "Install uv", + "Install exact Node.js", + "Install and verify exact npm toolchain", + "Run fixed absent/create/readback suite", + "Upload immutable absent/create/readback suite", + "Classify absent/create/readback completion", + "Require complete absent/create/readback evidence", + ), + "probe-exact-and-conflict": ( + "Check out reviewed target", + "Install uv", + "Install exact Node.js", + "Install and verify exact npm toolchain", + "Run fixed exact and conflict suite", + "Upload immutable exact/conflict suite", + "Classify exact/conflict completion", + "Require complete exact/conflict evidence", + ), + } + expected_commands = { + "probe-absent-create-readback": ( + "uv run --python 3.13 --package three-workflow-delivery-v3 \\\n" + " three-workflow-delivery-v3 governance " + "run-fixed-acceptance-probe \\\n" + " --suite absent-create-readback \\\n" + ' --package-coordinate "${INPUT_PACKAGE_COORDINATE}" \\\n' + ' --target-sha "${INPUT_TARGET_SHA}" \\\n' + " --timeout-seconds 120 \\\n" + ' --output ".wdv3/probe-absent-r${GITHUB_RUN_ID}-' + 'ra${GITHUB_RUN_ATTEMPT}.json" \\\n' + ' --github-output "${GITHUB_OUTPUT}"\n' + ), + "probe-exact-and-conflict": ( + "uv run --python 3.13 --package three-workflow-delivery-v3 \\\n" + " three-workflow-delivery-v3 governance " + "run-fixed-acceptance-probe \\\n" + " --suite exact-and-conflict \\\n" + ' --package-coordinate "${INPUT_PACKAGE_COORDINATE}" \\\n' + ' --target-sha "${INPUT_TARGET_SHA}" \\\n' + " --timeout-seconds 300 \\\n" + ' --output ".wdv3/probe-conflict-r${GITHUB_RUN_ID}-' + 'ra${GITHUB_RUN_ATTEMPT}.json" \\\n' + ' --github-output "${GITHUB_OUTPUT}"\n' + ), + } + npm_install = ( + "npm install --global npm@11.17.0\n" + 'test "$(node --version)" = "v24.19.0"\n' + 'test "$(npm --version)" = "11.17.0"\n' + ) + authorized_probe_steps = [] + for job_name in PROBE_JOBS: + job = jobs[job_name] + steps = _steps(job) + assert job["outputs"] == expected_outputs + assert tuple(step["name"] for step in steps) == expected_steps[job_name] + assert _step(job, "Check out reviewed target")["with"] == { + "ref": "${{ inputs.target_sha }}", + "persist-credentials": False, + } + assert _step(job, "Install uv")["with"] == { + "version": "0.12.5", + "github-token": "", + } + assert _step(job, "Install exact Node.js")["with"] == { + "node-version": "24.19.0" + } + assert _step(job, "Install and verify exact npm toolchain")["run"] == ( + npm_install + ) + probe_step = next(step for step in steps if step.get("id") == "probe") + assert probe_step["env"] == { + "WDV3_ACCEPTANCE_GITHUB_TOKEN": "${{ github.token }}", + "INPUT_PACKAGE_COORDINATE": "${{ inputs.package_coordinate }}", + "INPUT_TARGET_SHA": "${{ inputs.target_sha }}", + } + assert probe_step["run"] == expected_commands[job_name] + assert [step.get("id") for step in steps[-4:]] == [ + "probe", + "upload", + "classify", + None, + ] + authorized_probe_steps.append(probe_step) + + assert [ + step + for job in jobs.values() + for step in _steps(job) + if "WDV3_ACCEPTANCE_GITHUB_TOKEN" in step.get("env", {}) + ] == authorized_probe_steps + terminal_checkout = _step( + jobs["capture-governance-evidence"], + "Check out immutable workflow source", + ) + assert terminal_checkout["with"] == { + "ref": "${{ github.workflow_sha }}", + "persist-credentials": False, + "token": "${{ github.token }}", + } + assert _step( + jobs["capture-governance-evidence"], + "Install uv", + )["with"] == {"version": "0.12.5", "github-token": ""} + + +def test_retry_4_workflow_wires_terminal_governance_evidence_exactly() -> None: + document, _raw = _load_workflow() + jobs = _jobs(document) + terminal = jobs["capture-governance-evidence"] + terminal_steps = _steps(terminal) + + assert [step["name"] for step in terminal_steps] == [ + "Check out immutable workflow source", + "Install uv", + "Form and admit terminal Governance evidence", + "Upload immutable Governance acceptance evidence", + ] + evidence_step = terminal_steps[2] + assert evidence_step["env"] == { + "INPUT_TARGET_SHA": "${{ inputs.target_sha }}", + "INPUT_PACKAGE_COORDINATE": "${{ inputs.package_coordinate }}", + "VALIDATE_RESULT": "${{ needs.validate-fixed-inputs.result }}", + "REVIEW_RESULT": "${{ needs.acceptance-review.result }}", + "ABSENT_JOB_RESULT": ( + "${{ needs.probe-absent-create-readback.result }}" + ), + "CONFLICT_JOB_RESULT": ("${{ needs.probe-exact-and-conflict.result }}"), + "REVIEW_ARTIFACT_ID": ( + "${{ needs.acceptance-review.outputs.artifact-id }}" + ), + "ABSENT_RESULT": ( + "${{ needs.probe-absent-create-readback.outputs.result }}" + ), + "ABSENT_MUTATION_CLASSIFICATION": ( + "${{ needs.probe-absent-create-readback.outputs." + "mutation-classification }}" + ), + "ABSENT_SCENARIO_INVENTORY": ( + "${{ needs.probe-absent-create-readback.outputs." + "scenario-inventory }}" + ), + "ABSENT_RECORD_JSON": ( + "${{ needs.probe-absent-create-readback.outputs.record-json }}" + ), + "ABSENT_RECORD_DIGEST": ( + "${{ needs.probe-absent-create-readback.outputs.record-digest }}" + ), + "ABSENT_ARTIFACT_ID": ( + "${{ needs.probe-absent-create-readback.outputs.artifact-id }}" + ), + "ABSENT_ARTIFACT_DIGEST": ( + "${{ needs.probe-absent-create-readback.outputs.artifact-digest }}" + ), + "CONFLICT_RESULT": ( + "${{ needs.probe-exact-and-conflict.outputs.result }}" + ), + "CONFLICT_MUTATION_CLASSIFICATION": ( + "${{ needs.probe-exact-and-conflict.outputs." + "mutation-classification }}" + ), + "CONFLICT_SCENARIO_INVENTORY": ( + "${{ needs.probe-exact-and-conflict.outputs.scenario-inventory }}" + ), + "CONFLICT_RECORD_JSON": ( + "${{ needs.probe-exact-and-conflict.outputs.record-json }}" + ), + "CONFLICT_RECORD_DIGEST": ( + "${{ needs.probe-exact-and-conflict.outputs.record-digest }}" + ), + "CONFLICT_ARTIFACT_ID": ( + "${{ needs.probe-exact-and-conflict.outputs.artifact-id }}" + ), + "CONFLICT_ARTIFACT_DIGEST": ( + "${{ needs.probe-exact-and-conflict.outputs.artifact-digest }}" + ), + } + evidence_path = ( + ".wdv3/governance-acceptance-r${GITHUB_RUN_ID}-" + "ra${GITHUB_RUN_ATTEMPT}.json" + ) + assert f'export WDV3_FILE="{evidence_path}"' in evidence_step["run"] + + terminal_upload = terminal_steps[3] + assert terminal_upload["if"] == "${{ always() }}" + assert terminal_upload["uses"] == UPLOAD + assert terminal_upload["with"] == { + "name": ( + "wdv3-governance-acceptance-r${{ github.run_id }}-" + "ra${{ github.run_attempt }}" + ), + "path": ( + ".wdv3/governance-acceptance-r${{ github.run_id }}-" + "ra${{ github.run_attempt }}.json" + ), + "if-no-files-found": "error", + "include-hidden-files": True, + "retention-days": 45, + "overwrite": False, + "archive": False, + } + + script = _terminal_python(document) + tree = ast.parse(script) + expected = ast.literal_eval(_assigned_value(tree, "expected")) + assert expected == ( + ( + "probe-absent-create-readback", + ("absent-create-readback",), + "ABSENT", + ), + ( + "probe-exact-and-conflict", + ("exact", "identical-race", "differing-race", "lost-response"), + "CONFLICT", + ), + ) + + evidence_node = _assigned_value(tree, "document") + evidence_fields = _dict_fields(evidence_node) + assert tuple(evidence_fields) == ( + "schema", + "purpose", + "workflow", + "target-sha", + "package-coordinate", + "confirmation-digest", + "environment", + "reviewer", + "recovery", + "dependency-results", + "probe-facts", + "mutation-classification", + "producer", + "workflow-run-id", + "run-attempt", + "release-lineage", + ) + assert ast.literal_eval(evidence_fields["schema"]) == ( + "workflow-delivery/v3/governance-acceptance-evidence" + ) + assert ast.literal_eval(evidence_fields["purpose"]) == ( + "destination-acceptance" + ) + assert ast.literal_eval(evidence_fields["environment"]) == ENVIRONMENT + assert ast.literal_eval(evidence_fields["reviewer"]) == { + "login": None, + "source": "unavailable-in-job-context", + } + assert ast.literal_eval(evidence_fields["producer"]) == ( + "capture-governance-evidence" + ) + assert ast.literal_eval(evidence_fields["release-lineage"]) == "none" + assert ast.dump( + evidence_fields["confirmation-digest"], + include_attributes=False, + ) == ast.dump( + ast.parse( + '"sha256:" + hashlib.sha256(' + 'os.environ["WDV3_ACCEPTANCE_CONFIRMATION"].encode()' + ").hexdigest()", + mode="eval", + ).body, + include_attributes=False, + ) + + workflow_fields = _dict_fields(evidence_fields["workflow"]) + assert tuple(workflow_fields) == ("repository", "path", "ref", "sha") + assert ast.literal_eval(workflow_fields["path"]) == WORKFLOW_RELATIVE_PATH + recovery_fields = _dict_fields(evidence_fields["recovery"]) + assert tuple(recovery_fields) == ( + "workflow-run-id", + "environment", + "deployment", + "job", + "artifact-id", + ) + assert ast.literal_eval(recovery_fields["environment"]) == ENVIRONMENT + assert ast.literal_eval(recovery_fields["job"]) == "acceptance-review" + + call_names = [ + ast.unparse(call.func) + for call in ast.walk(tree) + if isinstance(call, ast.Call) + ] + assert ( + call_names.count("ValidatedAcceptanceRequestProof.from_closed_document") + == 1 + ) + assert call_names.count("AcceptanceRunnerDiagnostic") == 1 + assert call_names.count("admit_governance_acceptance_evidence") == 1 + assert call_names.count("canonicalize") == 2 + diagnostic_call = next( + call + for call in ast.walk(tree) + if isinstance(call, ast.Call) + and ast.unparse(call.func) == "AcceptanceRunnerDiagnostic" + ) + assert {keyword.arg for keyword in diagnostic_call.keywords} == { + "exit_classification", + "upstream_status", + "exception_category", + "request_correlation_digest", + } + assert script.count(f'"{BASE_COORDINATE}"') == 1 + assert script.count(f'"{CONFIRMATION}"') == 0 + assert script.count(f'"{ZERO_SHA}"') == 2 + assert 'record_digest = suite.to_document()["record-digest"]' in script + assert "record_digest != asserted_digest" in script + assert 'record_digest != record_json.get("record-digest")' in script + + +def test_retry_4_workflow_rejects_wrong_dispatch_inputs() -> None: + document, _raw = _load_workflow() + wrong_cases: dict[str, dict[str, str]] = { + "target": {"INPUT_TARGET_SHA": "e" * 40}, + "package-base": { + "INPUT_PACKAGE_COORDINATE": ( + f"{PACKAGE_NAME}@0.0.0-wdv3-acceptance.14" + ) + }, + "confirmation": {"INPUT_CONFIRM": "NOT_THE_RETRY_4_CONFIRMATION"}, + "protected-ref": {"GITHUB_REF": "refs/heads/not-main"}, + "run-attempt": {"GITHUB_RUN_ATTEMPT": "2"}, + } + + accepted = _run_fixed_input_guard( + document, + target_sha=TEST_ONLY_NONZERO_TARGET_SHA, + ) + rejected = { + name: _run_fixed_input_guard( + document, + target_sha=TEST_ONLY_NONZERO_TARGET_SHA, + overrides=overrides, + ) + for name, overrides in wrong_cases.items() + } + + assert (accepted.returncode, accepted.stdout, accepted.stderr) == ( + 0, + "", + "", + ) + assert { + name: (result.returncode, result.stdout, result.stderr) + for name, result in rejected.items() + } == dict.fromkeys(wrong_cases, (1, "", "")) + + +def test_retry_4_workflow_exposes_no_live_release_bypass_force_or_generalized_triggers() -> ( + None +): + document, raw = _load_workflow() + triggers = _triggers(document) + inputs = triggers["workflow_dispatch"]["inputs"] + jobs = _jobs(document) + terminal = _terminal_python(document) + + assert tuple(triggers) == ("workflow_dispatch",) + assert set(triggers).isdisjoint( + { + "workflow_call", + "schedule", + "push", + "pull_request", + "pull_request_target", + "repository_dispatch", + "workflow_run", + } + ) + assert tuple(inputs) == ("target_sha", "package_coordinate", "confirm") + assert set(inputs).isdisjoint( + { + "ref", + "channel", + "release_unit", + "suite", + "tag", + "environment", + "live", + "release", + "bypass", + "force", + } + ) + assert all("uses" not in job for job in jobs.values()) + assert re.search(r"\b(?:Live|Release)\b", raw) is None + assert re.search(r"(?i)\blive\b", raw) is None + assert re.search(r"(?i)\bbypass\b", raw) is None + assert re.search(r"(?i)\bforce\b", raw) is None + route_identifiers = { + "triggers": tuple(str(name) for name in triggers), + "inputs": tuple(str(name) for name in inputs), + "jobs": tuple(jobs), + "job-names": tuple(str(job.get("name", "")) for job in jobs.values()), + "step-names": tuple( + str(step.get("name", "")) + for job in jobs.values() + for step in _steps(job) + ), + } + route_pattern = re.compile(r"(?i)(?:^|[\s_-])(?:live|release)(?:$|[\s_-])") + assert { + surface: tuple( + identifier + for identifier in identifiers + if route_pattern.search(identifier) + ) + for surface, identifiers in route_identifiers.items() + } == dict.fromkeys(route_identifiers, ()) + assert re.findall( + ( + r"(?m)^\s*three-workflow-delivery-v3\s+" + r"([a-z][a-z0-9-]*)\b" + ), + raw, + ) == ["governance", "governance"] + assert '"release-lineage": "none"' in terminal + assert "--channel" not in raw + assert "--release-unit" not in raw + assert "--bypass" not in raw + assert "--force" not in raw + assert "live_enabled" not in raw + assert document["env"]["WDV3_PURPOSE"] == "destination-acceptance" + + +def test_retry_4_terminal_program_preserves_fixed_identity_after_rejected_dispatch( + tmp_path: Path, +) -> None: + import sys # noqa: PLC0415 + + from three_workflow_delivery_v3.canonical import ( # noqa: PLC0415 + canonicalize, + ) + from three_workflow_delivery_v3.records.governance import ( # noqa: PLC0415 + admit_governance_acceptance_evidence, + ) + + document, _raw = _load_workflow() + program = _terminal_python(document) + evidence_path = tmp_path / "governance-acceptance.json" + empty_home = tmp_path / "empty-home" + empty_home.mkdir() + run_id = 608 + workflow_sha = "a" * 40 + environment = { + "HOME": str(empty_home), + "PATH": os.environ.get("PATH", os.defpath), + "PYTHONNOUSERSITE": "1", + "USERPROFILE": str(empty_home), + "WDV3_FILE": str(evidence_path), + "INPUT_TARGET_SHA": "e" * 40, + "INPUT_PACKAGE_COORDINATE": ( + f"{PACKAGE_NAME}@0.0.0-wdv3-acceptance.17" + ), + "WDV3_ACCEPTANCE_TARGET_SHA": ZERO_SHA, + "WDV3_ACCEPTANCE_PACKAGE_COORDINATE": BASE_COORDINATE, + "WDV3_ACCEPTANCE_CONFIRMATION": CONFIRMATION, + "VALIDATE_RESULT": "failure", + "REVIEW_RESULT": "skipped", + "ABSENT_JOB_RESULT": "skipped", + "CONFLICT_JOB_RESULT": "skipped", + "REVIEW_ARTIFACT_ID": "", + "ABSENT_RESULT": "", + "ABSENT_MUTATION_CLASSIFICATION": "", + "ABSENT_SCENARIO_INVENTORY": "", + "ABSENT_RECORD_JSON": "", + "ABSENT_RECORD_DIGEST": "", + "ABSENT_ARTIFACT_ID": "", + "ABSENT_ARTIFACT_DIGEST": "", + "CONFLICT_RESULT": "", + "CONFLICT_MUTATION_CLASSIFICATION": "", + "CONFLICT_SCENARIO_INVENTORY": "", + "CONFLICT_RECORD_JSON": "", + "CONFLICT_RECORD_DIGEST": "", + "CONFLICT_ARTIFACT_ID": "", + "CONFLICT_ARTIFACT_DIGEST": "", + "GITHUB_RUN_ATTEMPT": "1", + "GITHUB_RUN_ID": str(run_id), + "GITHUB_REPOSITORY": "hcoona/three", + "GITHUB_REF": "refs/heads/main", + "GITHUB_WORKFLOW_SHA": workflow_sha, + } + if system_root := os.environ.get("SYSTEMROOT"): + environment["SYSTEMROOT"] = system_root + if "INPUT_CONFIRM" in program: + environment["INPUT_CONFIRM"] = "NOT_THE_RETRY_4_CONFIRMATION" + + result = subprocess.run( + (sys.executable, "-I", "-c", program), + check=False, + capture_output=True, + text=True, + env=environment, + ) + + assert (result.returncode, result.stdout, result.stderr) == (0, "", "") + assert evidence_path.is_file() + evidence = evidence_path.read_bytes() + admitted = admit_governance_acceptance_evidence(evidence) + assert evidence == canonicalize(admitted.to_document()) + assert admitted.workflow.to_document() == { + "repository": "hcoona/three", + "path": WORKFLOW_RELATIVE_PATH, + "ref": "refs/heads/main", + "sha": workflow_sha, + } + assert (admitted.target_sha, admitted.package_coordinate) == ( + ZERO_SHA, + BASE_COORDINATE, + ) + assert admitted.confirmation_digest == CONFIRMATION_DIGEST + assert admitted.environment == ENVIRONMENT + assert admitted.reviewer_record.to_document() == { + "login": None, + "source": "unavailable-in-job-context", + } + assert admitted.recovery.to_document() == { + "workflow-run-id": run_id, + "environment": ENVIRONMENT, + "deployment": f"run:{run_id}/environment:acceptance", + "job": "acceptance-review", + "artifact-id": None, + } + assert tuple( + result.to_document() for result in admitted.dependency_results + ) == ( + {"job": "validate-fixed-inputs", "result": "failure"}, + {"job": "acceptance-review", "result": "skipped"}, + {"job": "probe-absent-create-readback", "result": "skipped"}, + {"job": "probe-exact-and-conflict", "result": "skipped"}, + ) + assert tuple(fact.to_document() for fact in admitted.probe_facts) == ( + { + "probe": "probe-absent-create-readback", + "result": "incomplete", + "scenario-inventory": ["absent-create-readback"], + "record-digest": None, + "artifact-id": None, + "artifact-digest": None, + "scenarios": [], + }, + { + "probe": "probe-exact-and-conflict", + "result": "incomplete", + "scenario-inventory": [ + "exact", + "identical-race", + "differing-race", + "lost-response", + ], + "record-digest": None, + "artifact-id": None, + "artifact-digest": None, + "scenarios": [], + }, + ) + assert admitted.mutation_classification == "incomplete" + assert (admitted.workflow_run_id, admitted.run_attempt) == (run_id, 1) diff --git a/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py b/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py index cc27add9..8fb3c9df 100644 --- a/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py +++ b/src/public/lib/three-workflow-delivery-v3/tests/contracts/test_commit11_legacy_buddy_retirement.py @@ -689,12 +689,76 @@ def test_codeowners_covers_deleted_and_future_buddy_routes() -> None: } == dict.fromkeys(paths, ("@hcoona",)) -def test_temporary_acceptance_workflows_are_retired() -> None: - """Retire every temporary destination-acceptance workflow source.""" - paths = tuple( +def test_retry_4_is_the_only_temporary_workflow_allowed_by_legacy_buddy_retirement() -> ( # noqa: E501 + None +): + """Permit only the exact retry-4 preparation workflow.""" + retry_4 = ( + WORKFLOWS / "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" + ) + temporary_workflows = tuple( + sorted( + { + *WORKFLOWS.glob( + "workflow-delivery-v3-buddy-smoke-acceptance*.yml" + ), + *WORKFLOWS.glob( + "workflow-delivery-v3-buddy-smoke-acceptance*.yaml" + ), + } + ) + ) + + assert temporary_workflows == (retry_4,), ( + "E-WORKFLOW-ABSENT: required retry-4 workflow is absent at " + ".github/workflows/" + "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" + if retry_4 not in temporary_workflows + else ( + "legacy Buddy retirement permits only the exact retry-4 " + f"temporary workflow: {temporary_workflows!r}" + ) + ) + + +def test_legacy_buddy_retirement_rejects_every_other_temporary_and_legacy_workflow() -> ( # noqa: E501 + None +): + """Reject legacy identities without broadening the retry-4 exception.""" + retry_4 = ( + WORKFLOWS / "workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" + ) + explicitly_retired = tuple( sorted( - WORKFLOWS.glob("workflow-delivery-v3-buddy-smoke-acceptance*.yml") + { + *(REPO_ROOT / path for path in LEGACY_ENTRY_PATHS), + *( + WORKFLOWS / basename + for basename in FORBIDDEN_COMPATIBILITY_BASENAMES + ), + WORKFLOWS / "workflow-delivery-v3-buddy-smoke-acceptance.yml", + WORKFLOWS + / "workflow-delivery-v3-buddy-smoke-acceptance-retry-2.yml", + WORKFLOWS + / "workflow-delivery-v3-buddy-smoke-acceptance-retry-3.yml", + WORKFLOWS + / "workflow-delivery-v3-buddy-smoke-acceptance-retry-5.yml", + } + ) + ) + temporary_workflows = tuple( + sorted( + { + *WORKFLOWS.glob( + "workflow-delivery-v3-buddy-smoke-acceptance*.yml" + ), + *WORKFLOWS.glob( + "workflow-delivery-v3-buddy-smoke-acceptance*.yaml" + ), + } ) ) - assert paths == () + assert tuple(path for path in explicitly_retired if path.exists()) == () + assert tuple(path for path in temporary_workflows if path != retry_4) == () + assert _legacy_buddy_routes(WORKFLOWS) == () diff --git a/src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py b/src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py index 32a72df2..9359b3bb 100644 --- a/src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py +++ b/src/public/lib/three-workflow-delivery-v3/tests/governance/test_commit10_acceptance_evidence.py @@ -6,7 +6,7 @@ import itertools from copy import deepcopy -from typing import Any +from typing import Any, cast import pytest import three_workflow_delivery_v3.records.governance as governance_module @@ -3075,3 +3075,960 @@ def test_governance_proof_required_completion_rejects_diagnostic_only_authority( assert scenario["runner-diagnostic"] == diagnostic with pytest.raises(ValueError, match="validated-request-proof"): _admit(document) + + +TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE = ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13" +) +TEST_LOCAL_RETRY_4_WORKFLOW_PATH = ( + ".github/workflows/workflow-delivery-v3-buddy-smoke-acceptance-retry-4.yml" +) +TEST_LOCAL_RETRY_4_ENVIRONMENT = ( + "workflow-delivery-v3-buddy-smoke-acceptance-retry-4" +) +TEST_LOCAL_RETRY_4_CONFIRMATION = ( + "I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4" +) +TEST_LOCAL_RETRY_4_CONFIRMATION_DIGEST = ( + "sha256:b6f94d3c13c98b0714404959dd878230f8302ee849038a536f5a18cc3a85c7ec" +) +TEST_LOCAL_RETRY_4_PREPARATION_TARGET = "0" * 40 +TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA = "d" * 40 +TEST_LOCAL_RETRY_4_SCENARIO_COORDINATES = { + "absent-create-readback": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + "exact": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + "wdv3-acceptance-13", + ), + "identical-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.14", + "wdv3-acceptance-14", + ), + "differing-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.15", + "wdv3-acceptance-15", + ), + "lost-response": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.16", + "wdv3-acceptance-16", + ), +} +TEST_LOCAL_RETRY_2_SCENARIO_COORDINATES = { + "absent-create-readback": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "wdv3-acceptance-5", + ), + "exact": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "wdv3-acceptance-5", + ), + "identical-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.6", + "wdv3-acceptance-6", + ), + "differing-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.7", + "wdv3-acceptance-7", + ), + "lost-response": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.8", + "wdv3-acceptance-8", + ), +} +TEST_LOCAL_RETRY_3_SCENARIO_COORDINATES = { + "absent-create-readback": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "wdv3-acceptance-9", + ), + "exact": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "wdv3-acceptance-9", + ), + "identical-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.10", + "wdv3-acceptance-10", + ), + "differing-race": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.11", + "wdv3-acceptance-11", + ), + "lost-response": ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.12", + "wdv3-acceptance-12", + ), +} +TEST_LOCAL_HISTORICAL_FINALIZED_SUITE_DIGESTS = { + "retry-1": ( + HISTORICAL_ABSENT_CREATE_READBACK_RECORD_DIGEST, + HISTORICAL_EXACT_AND_CONFLICT_RECORD_DIGEST, + ), + "retry-2": ( + "sha256:7c19112cbadf98ea9b0fa4b2fc936ac35974dbb87fe08910ff4e4fec0be35ed9", + "sha256:5f7c871d72a61e08550bca6c39d9821f9dd386439719bb0980f618fa2e77b732", + ), + "retry-3": ( + "sha256:0ca1c4578e8102918cc2014a24d5a1510953ed7ece5afd5f575e3451c9fc40d7", + "sha256:4756bbc634b750d62caf8f2edcec4a251dd0195ffbad4ec17ff379bed94ffc0f", + ), +} + + +def _test_local_retry_4_governance_profile( + *, + target_sha: str, +) -> Any: + return governance_module._GovernanceAcceptanceProfile( + package_coordinate=TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE, + workflow_path=TEST_LOCAL_RETRY_4_WORKFLOW_PATH, + environment=TEST_LOCAL_RETRY_4_ENVIRONMENT, + target_sha=target_sha, + confirmation_digest=TEST_LOCAL_RETRY_4_CONFIRMATION_DIGEST, + scenario_coordinates=tuple( + ( + scenario, + *TEST_LOCAL_RETRY_4_SCENARIO_COORDINATES[scenario], + ) + for scenario in GOVERNANCE_ACCEPTANCE_SCENARIOS + ), + ) + + +def _registered_retry_4_governance_profile() -> Any: + matches = tuple( + profile + for profile in governance_module._GOVERNANCE_ACCEPTANCE_PROFILES + if profile.package_coordinate == TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE + ) + if not matches: + pytest.fail( + "E-GOVERNANCE-PROFILE-ABSENT: the fourth reviewed Governance " + "acceptance profile is not registered", + pytrace=False, + ) + assert len(matches) == 1, ( + "the fourth reviewed Governance acceptance profile must be unique" + ) + return matches[0] + + +def _retry_4_preparation_document() -> dict[str, Any]: + document = _retry_3_document() + document["workflow"]["path"] = TEST_LOCAL_RETRY_4_WORKFLOW_PATH + document["target-sha"] = TEST_LOCAL_RETRY_4_PREPARATION_TARGET + document["package-coordinate"] = TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE + document["confirmation-digest"] = TEST_LOCAL_RETRY_4_CONFIRMATION_DIGEST + document["environment"] = TEST_LOCAL_RETRY_4_ENVIRONMENT + document["recovery"]["environment"] = TEST_LOCAL_RETRY_4_ENVIRONMENT + return document + + +def _test_local_proof_document( + *, + package_coordinate: str, + tag: str, + label: str, +) -> dict[str, Any]: + template = ValidatedAcceptanceRequestProof.from_validated_exchange( + raw_request=(f'{{"_id":"{label}"}}').encode(), + tarball=f"{label}-tarball".encode(), + package_coordinate=COORDINATE, + tag="wdv3-acceptance-1", + upstream_status=201, + selected_headers={ + "Content-Type": "application/json", + "ETag": f'"{label}"', + }, + response_body=(f'{{"ok":true,"proof":"{label}"}}').encode(), + ).to_document() + template["package-coordinate"] = package_coordinate + template["tag"] = tag + return template + + +def _test_local_finalized_document( + *, + workflow_path: str, + target_sha: str, + package_coordinate: str, + confirmation_digest: str, + environment: str, + scenario_coordinates: dict[str, tuple[str, str]], + proof_namespace: str, +) -> dict[str, Any]: + document = _document() + document["workflow"]["path"] = workflow_path + document["target-sha"] = target_sha + document["package-coordinate"] = package_coordinate + document["confirmation-digest"] = confirmation_digest + document["environment"] = environment + document["recovery"]["environment"] = environment + proof_documents = { + scenario: _test_local_proof_document( + package_coordinate=scenario_coordinates[scenario][0], + tag=scenario_coordinates[scenario][1], + label=f"{proof_namespace}-{scenario}", + ) + for scenario in ("absent-create-readback", "lost-response") + } + for probe_index, fact in enumerate(document["probe-facts"]): + for scenario_document in fact["scenarios"]: + scenario = scenario_document["scenario"] + coordinate, tag = scenario_coordinates[scenario] + scenario_document["package-coordinate"] = coordinate + scenario_document["tag"] = tag + scenario_document.pop("validated-request-proof", None) + if scenario == "absent-create-readback": + proof = proof_documents[scenario] + scenario_document["response"]["result"] = "protocol-confirmed" + scenario_document["response"]["identity-digest"] = proof[ + "response-identity-digest" + ] + scenario_document["response"]["diagnostics"] = [] + scenario_document["post"]["content-sha512"] = proof[ + "tarball-sha512" + ] + scenario_document["validated-request-proof"] = proof + elif scenario == "lost-response": + proof = proof_documents[scenario] + scenario_document["response"]["identity-digest"] = proof[ + "response-identity-digest" + ] + scenario_document["post"]["content-sha512"] = proof[ + "tarball-sha512" + ] + scenario_document["validated-request-proof"] = proof + _refresh_probe_record_digest_unchecked(document, probe_index) + return document + + +def test_retry_4_governance_profiles_have_stable_historical_order_and_unique_base_coordinates() -> ( + None +): + retry_4_profile = _registered_retry_4_governance_profile() + profiles = governance_module._GOVERNANCE_ACCEPTANCE_PROFILES + base_coordinates = tuple(profile.package_coordinate for profile in profiles) + + assert base_coordinates == ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.1", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.13", + ) + assert len(base_coordinates) == len(set(base_coordinates)) == len(profiles) + assert profiles[-1] is retry_4_profile + + +def test_retry_4_governance_profile_binds_exact_workflow_environment_confirmation_digest_and_scenarios() -> ( + None +): + profile = _registered_retry_4_governance_profile() + + assert profile.package_coordinate == TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE + assert profile.workflow_path == TEST_LOCAL_RETRY_4_WORKFLOW_PATH + assert profile.environment == TEST_LOCAL_RETRY_4_ENVIRONMENT + assert profile.confirmation_digest == ( + TEST_LOCAL_RETRY_4_CONFIRMATION_DIGEST + ) + assert TEST_LOCAL_RETRY_4_CONFIRMATION == ( + "I_ACCEPT_DISPOSABLE_GITHUB_PACKAGES_PROBES_RETRY_4" + ) + assert ( + ValidatedAcceptanceRequestProof._sha256( + TEST_LOCAL_RETRY_4_CONFIRMATION.encode("ascii") + ) + == TEST_LOCAL_RETRY_4_CONFIRMATION_DIGEST + ) + assert tuple(profile.coordinates().items()) == tuple( + TEST_LOCAL_RETRY_4_SCENARIO_COORDINATES.items() + ) + assert tuple(profile.coordinates()) == GOVERNANCE_ACCEPTANCE_SCENARIOS + assert profile.target_sha == "0" * 40 + assert profile.target_sha.encode("ascii") == b"0" * 40 + + +def test_retry_4_governance_admits_exact_zero_target_rejected_dispatch() -> ( + None +): + _registered_retry_4_governance_profile() + document = _retry_4_preparation_document() + + admitted = _admit(document) + admitted_document = admitted.to_document() + + assert document["target-sha"].encode("ascii") == b"0" * 40 + assert admitted.target_sha == TEST_LOCAL_RETRY_4_PREPARATION_TARGET + assert [ + (result.job, result.result) for result in admitted.dependency_results + ] == [ + ("validate-fixed-inputs", "failure"), + ("acceptance-review", "skipped"), + ("probe-absent-create-readback", "skipped"), + ("probe-exact-and-conflict", "skipped"), + ] + assert admitted.mutation_classification == "incomplete" + assert admitted.recovery.artifact_id is None + assert admitted.reviewer is None + assert admitted.reviewer_source == "unavailable-in-job-context" + assert [ + ( + fact.result, + fact.record_digest, + fact.artifact_id, + fact.artifact_digest, + fact.scenarios, + ) + for fact in admitted.probe_facts + ] == [ + ("incomplete", None, None, None, ()), + ("incomplete", None, None, None, ()), + ] + assert admitted_document == document + assert canonicalize(admitted_document) == canonicalize(document) + + +@pytest.mark.parametrize( + ("target_sha", "message"), + [ + pytest.param( + "0" * 39, + "40 lowercase hexadecimal", + id="39-ascii-zeroes", + ), + pytest.param( + "0" * 41, + "40 lowercase hexadecimal", + id="41-ascii-zeroes", + ), + pytest.param( + "\uff10" * 40, + "40 lowercase hexadecimal", + id="40-non-ascii-zeroes", + ), + pytest.param( + ("0" * 39) + "1", + "reviewed acceptance profile", + id="40-hex-with-nonzero-nibble", + ), + ], +) +def test_retry_4_governance_rejects_non_exact_zero_targets( + target_sha: str, + message: str, +) -> None: + profile = _registered_retry_4_governance_profile() + if target_sha == profile.target_sha: + target_sha = ("0" * 39) + "2" + document = _retry_4_preparation_document() + document["target-sha"] = target_sha + + with pytest.raises(ValueError, match=message): + _admit(document) + + +@pytest.mark.parametrize( + ("path", "value", "message"), + [ + pytest.param( + ("dependency-results", 1, "result"), + "success", + "zero target-sha requires exact rejected", + id="environment-review-ran", + ), + pytest.param( + ("dependency-results", 2, "result"), + "success", + "zero target-sha requires exact rejected", + id="absent-create-probe-ran", + ), + pytest.param( + ("dependency-results", 3, "result"), + "success", + "zero target-sha requires exact rejected", + id="exact-and-conflict-probe-ran", + ), + pytest.param( + ("probe-facts", 0, "record-digest"), + SHA256_A, + "retain suite records", + id="probe-record-digest-retained", + ), + pytest.param( + ("probe-facts", 0, "scenarios"), + "test-local-retained-scenario", + "record-digest", + id="probe-scenario-retained", + ), + pytest.param( + ("probe-facts", 0, "artifact-id"), + 799, + "zero target-sha requires exact rejected", + id="probe-artifact-id-retained", + ), + pytest.param( + ("probe-facts", 0, "artifact-digest"), + SHA256_A, + "zero target-sha requires exact rejected", + id="probe-artifact-digest-retained", + ), + pytest.param( + ("recovery", "artifact-id"), + 701, + "zero target-sha requires exact rejected", + id="review-artifact-retained", + ), + pytest.param( + ("reviewer",), + { + "login": "octocat", + "source": "on-demand-read-only-inspection", + }, + "zero target-sha requires exact rejected", + id="reviewer-attributed", + ), + pytest.param( + ("mutation-classification",), + "unknown", + "mutation-classification", + id="possible-mutation-claimed", + ), + ], +) +def test_retry_4_zero_target_rejects_review_probe_record_artifact_reviewer_or_mutation_claims( + path: tuple[object, ...], + value: object, + message: str, +) -> None: + _registered_retry_4_governance_profile() + document = _retry_4_preparation_document() + if value == "test-local-retained-scenario": + retained_scenario = _scenario("absent-create-readback") + retained_scenario["package-coordinate"], retained_scenario["tag"] = ( + TEST_LOCAL_RETRY_4_SCENARIO_COORDINATES["absent-create-readback"] + ) + value = [retained_scenario] + _set_path(document, path, value) + + with pytest.raises(ValueError, match=message): + _admit(document) + + +def test_retry_4_finalized_placeholder_round_trips_canonically_with_exact_bindings( + monkeypatch: pytest.MonkeyPatch, +) -> None: + profile = _test_local_retry_4_governance_profile( + target_sha=TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA + ) + historical_profiles = tuple( + existing + for existing in governance_module._GOVERNANCE_ACCEPTANCE_PROFILES + if existing.package_coordinate != TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE + ) + assert tuple( + existing.package_coordinate for existing in historical_profiles + ) == ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.1", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + ) + monkeypatch.setattr( + governance_module, + "_GOVERNANCE_ACCEPTANCE_PROFILES", + (*historical_profiles, profile), + ) + document = _test_local_finalized_document( + workflow_path=TEST_LOCAL_RETRY_4_WORKFLOW_PATH, + target_sha=TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA, + package_coordinate=TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE, + confirmation_digest=TEST_LOCAL_RETRY_4_CONFIRMATION_DIGEST, + environment=TEST_LOCAL_RETRY_4_ENVIRONMENT, + scenario_coordinates=TEST_LOCAL_RETRY_4_SCENARIO_COORDINATES, + proof_namespace="test-only-retry-4-placeholder", + ) + + raw = canonicalize(document) + admitted = admit_governance_acceptance_evidence(raw) + admitted_document = admitted.to_document() + probe_facts = cast("list[dict[str, Any]]", admitted_document["probe-facts"]) + scenarios = { + scenario["scenario"]: scenario + for fact in probe_facts + for scenario in cast("list[dict[str, Any]]", fact["scenarios"]) + } + + assert TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA == "d" * 40 + assert TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA != ( + TEST_LOCAL_RETRY_4_PREPARATION_TARGET + ) + assert admitted.target_sha == TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA + assert raw == canonicalize(admitted_document) + assert admitted_document == document + assert admitted.evidence_digest == canonical_sha256(document) + assert [ + ( + scenario, + scenarios[scenario]["package-coordinate"], + scenarios[scenario]["tag"], + ) + for scenario in GOVERNANCE_ACCEPTANCE_SCENARIOS + ] == [ + (scenario, *TEST_LOCAL_RETRY_4_SCENARIO_COORDINATES[scenario]) + for scenario in GOVERNANCE_ACCEPTANCE_SCENARIOS + ] + for scenario in ("absent-create-readback", "lost-response"): + scenario_document = scenarios[scenario] + proof = scenario_document["validated-request-proof"] + assert ( + proof["package-coordinate"], + proof["tag"], + ) == TEST_LOCAL_RETRY_4_SCENARIO_COORDINATES[scenario] + assert ( + scenario_document["response"]["identity-digest"] + == proof["response-identity-digest"] + ) + assert ( + scenario_document["post"]["content-sha512"] + == proof["tarball-sha512"] + ) + + +@pytest.mark.parametrize( + ("document_profile", "field"), + [ + pytest.param( + "retry-4", + "workflow", + id="retry-4-document-with-retry-3-workflow", + ), + pytest.param( + "retry-4", + "environment", + id="retry-4-document-with-retry-3-environment", + ), + pytest.param( + "retry-4", + "recovery-environment", + id="retry-4-document-with-retry-3-recovery-environment", + ), + pytest.param( + "retry-4", + "confirmation-digest", + id="retry-4-document-with-retry-3-confirmation-digest", + ), + pytest.param( + "retry-4", + "target", + id="retry-4-document-with-retry-3-target", + ), + pytest.param( + "retry-4", + "coordinate", + id="retry-4-document-with-retry-3-coordinate", + ), + pytest.param( + "retry-4", + "tag", + id="retry-4-document-with-retry-3-tag", + ), + pytest.param( + "retry-3", + "workflow", + id="retry-3-document-with-retry-4-workflow", + ), + pytest.param( + "retry-3", + "environment", + id="retry-3-document-with-retry-4-environment", + ), + pytest.param( + "retry-3", + "recovery-environment", + id="retry-3-document-with-retry-4-recovery-environment", + ), + pytest.param( + "retry-3", + "confirmation-digest", + id="retry-3-document-with-retry-4-confirmation-digest", + ), + pytest.param( + "retry-3", + "target", + id="retry-3-document-with-retry-4-target", + ), + pytest.param( + "retry-3", + "coordinate", + id="retry-3-document-with-retry-4-coordinate", + ), + pytest.param( + "retry-3", + "tag", + id="retry-3-document-with-retry-4-tag", + ), + ], +) +def test_retry_4_governance_rejects_cross_profile_field_substitutions( + document_profile: str, + field: str, + monkeypatch: pytest.MonkeyPatch, +) -> None: + registered_retry_4_profile = _registered_retry_4_governance_profile() + assert ( + registered_retry_4_profile.target_sha + == TEST_LOCAL_RETRY_4_PREPARATION_TARGET + ) + paths = { + "workflow": ("workflow", "path"), + "environment": ("environment",), + "recovery-environment": ("recovery", "environment"), + "confirmation-digest": ("confirmation-digest",), + "target": ("target-sha",), + "coordinate": ( + "probe-facts", + 0, + "scenarios", + 0, + "package-coordinate", + ), + "tag": ("probe-facts", 0, "scenarios", 0, "tag"), + } + messages = { + "workflow": "workflow.path", + "environment": "environment", + "recovery-environment": "recovery environment", + "confirmation-digest": "confirmation-digest", + "target": "target-sha", + "coordinate": "package-coordinate", + "tag": "tag", + } + retry_3_values = { + "workflow": GOVERNANCE_RETRY_3_ACCEPTANCE_WORKFLOW_PATH, + "environment": GOVERNANCE_RETRY_3_ACCEPTANCE_ENVIRONMENT, + "recovery-environment": GOVERNANCE_RETRY_3_ACCEPTANCE_ENVIRONMENT, + "confirmation-digest": ( + "sha256:" + "33e59948941f5f1111d5017ab80dd33c90dd2ac8d1a17203e7f7382a8c5b2c72" + ), + "target": RETRY_3_TARGET_SHA, + "coordinate": TEST_LOCAL_RETRY_3_SCENARIO_COORDINATES[ + "absent-create-readback" + ][0], + "tag": TEST_LOCAL_RETRY_3_SCENARIO_COORDINATES[ + "absent-create-readback" + ][1], + } + retry_4_values = { + "workflow": registered_retry_4_profile.workflow_path, + "environment": registered_retry_4_profile.environment, + "recovery-environment": registered_retry_4_profile.environment, + "confirmation-digest": registered_retry_4_profile.confirmation_digest, + "target": registered_retry_4_profile.target_sha, + "coordinate": registered_retry_4_profile.coordinates()[ + "absent-create-readback" + ][0], + "tag": registered_retry_4_profile.coordinates()[ + "absent-create-readback" + ][1], + } + if document_profile == "retry-4": + if field in {"coordinate", "tag"}: + finalized_retry_4_profile = _test_local_retry_4_governance_profile( + target_sha=TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA + ) + historical_profiles = tuple( + existing + for existing in governance_module._GOVERNANCE_ACCEPTANCE_PROFILES + if existing.package_coordinate + != TEST_LOCAL_RETRY_4_PACKAGE_COORDINATE + ) + monkeypatch.setattr( + governance_module, + "_GOVERNANCE_ACCEPTANCE_PROFILES", + (*historical_profiles, finalized_retry_4_profile), + ) + document = _test_local_finalized_document( + workflow_path=finalized_retry_4_profile.workflow_path, + target_sha=finalized_retry_4_profile.target_sha, + package_coordinate=finalized_retry_4_profile.package_coordinate, + confirmation_digest=( + finalized_retry_4_profile.confirmation_digest + ), + environment=finalized_retry_4_profile.environment, + scenario_coordinates=finalized_retry_4_profile.coordinates(), + proof_namespace="test-only-retry-4-cross-profile", + ) + assert ( + finalized_retry_4_profile.target_sha + == TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA + ) + assert ( + finalized_retry_4_profile.target_sha + != registered_retry_4_profile.target_sha + ) + else: + document = _retry_4_preparation_document() + replacement = retry_3_values[field] + else: + document = _test_local_finalized_document( + workflow_path=GOVERNANCE_RETRY_3_ACCEPTANCE_WORKFLOW_PATH, + target_sha=RETRY_3_TARGET_SHA, + package_coordinate=GOVERNANCE_RETRY_3_ACCEPTANCE_PACKAGE_COORDINATE, + confirmation_digest=( + "sha256:" + "33e59948941f5f1111d5017ab80dd33c90dd2ac8d1a17203e7f7382a8c5b2c72" + ), + environment=GOVERNANCE_RETRY_3_ACCEPTANCE_ENVIRONMENT, + scenario_coordinates=TEST_LOCAL_RETRY_3_SCENARIO_COORDINATES, + proof_namespace="historical-retry-3-control", + ) + replacement = retry_4_values[field] + + admitted_control = _admit(document) + assert admitted_control.to_document() == document + if document_profile == "retry-4" and field not in {"coordinate", "tag"}: + assert ( + admitted_control.target_sha == TEST_LOCAL_RETRY_4_PREPARATION_TARGET + ) + assert admitted_control.mutation_classification == "incomplete" + else: + expected_target = ( + TEST_ONLY_RETRY_4_FINALIZED_TARGET_SHA + if document_profile == "retry-4" + else RETRY_3_TARGET_SHA + ) + assert admitted_control.target_sha == expected_target + assert admitted_control.mutation_classification == "complete" + mutated = deepcopy(document) + _set_path(mutated, paths[field], replacement) + assert mutated != document + with pytest.raises(ValueError, match=messages[field]): + _admit(mutated) + + +def test_retry_4_governance_preserves_historical_profiles_digests_and_replay_evidence() -> ( + None +): + historical_profile_tuples = tuple( + ( + profile.package_coordinate, + profile.workflow_path, + profile.environment, + profile.target_sha, + profile.confirmation_digest, + profile.scenario_coordinates, + ) + for profile in governance_module._GOVERNANCE_ACCEPTANCE_PROFILES[:3] + ) + assert historical_profile_tuples == ( + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.1", + ( + ".github/workflows/" + "workflow-delivery-v3-buddy-smoke-acceptance.yml" + ), + "workflow-delivery-v3-buddy-smoke-acceptance", + "5a84bebd05407e1859fe76f400dcb4f4cbcd002e", + ( + "sha256:" + "6ab9696b51f21083802af68d80104f65ffb844bdcd449974c881e5a8cc96ad5e" + ), + ( + ( + "absent-create-readback", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.1", + "wdv3-acceptance-1", + ), + ( + "exact", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.1", + "wdv3-acceptance-1", + ), + ( + "identical-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.2", + "wdv3-acceptance-2", + ), + ( + "differing-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.3", + "wdv3-acceptance-3", + ), + ( + "lost-response", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.4", + "wdv3-acceptance-4", + ), + ), + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + ( + ".github/workflows/" + "workflow-delivery-v3-buddy-smoke-acceptance-retry-2.yml" + ), + "workflow-delivery-v3-buddy-smoke-acceptance-retry-2", + "b031e5e0bd98a95943a03a1529b64e856e1a8aa1", + ( + "sha256:" + "1215f9d01cd343462c3f826ba67ebee86b6f6142b7fcfe5630572a5a808314f8" + ), + ( + ( + "absent-create-readback", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "wdv3-acceptance-5", + ), + ( + "exact", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5", + "wdv3-acceptance-5", + ), + ( + "identical-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.6", + "wdv3-acceptance-6", + ), + ( + "differing-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.7", + "wdv3-acceptance-7", + ), + ( + "lost-response", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.8", + "wdv3-acceptance-8", + ), + ), + ), + ( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + ( + ".github/workflows/" + "workflow-delivery-v3-buddy-smoke-acceptance-retry-3.yml" + ), + "workflow-delivery-v3-buddy-smoke-acceptance-retry-3", + RETRY_3_TARGET_SHA, + ( + "sha256:" + "33e59948941f5f1111d5017ab80dd33c90dd2ac8d1a17203e7f7382a8c5b2c72" + ), + ( + ( + "absent-create-readback", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "wdv3-acceptance-9", + ), + ( + "exact", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.9", + "wdv3-acceptance-9", + ), + ( + "identical-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.10", + "wdv3-acceptance-10", + ), + ( + "differing-race", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.11", + "wdv3-acceptance-11", + ), + ( + "lost-response", + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.12", + "wdv3-acceptance-12", + ), + ), + ), + ) + documents = { + "retry-1": _document(), + "retry-2": _test_local_finalized_document( + workflow_path=( + ".github/workflows/" + "workflow-delivery-v3-buddy-smoke-acceptance-retry-2.yml" + ), + target_sha="b031e5e0bd98a95943a03a1529b64e856e1a8aa1", + package_coordinate=( + "@hcoona/hcoona-release-smoke-npm@0.0.0-wdv3-acceptance.5" + ), + confirmation_digest=( + "sha256:" + "1215f9d01cd343462c3f826ba67ebee86b6f6142b7fcfe5630572a5a808314f8" + ), + environment=("workflow-delivery-v3-buddy-smoke-acceptance-retry-2"), + scenario_coordinates=TEST_LOCAL_RETRY_2_SCENARIO_COORDINATES, + proof_namespace="historical-retry-2", + ), + "retry-3": _test_local_finalized_document( + workflow_path=GOVERNANCE_RETRY_3_ACCEPTANCE_WORKFLOW_PATH, + target_sha=RETRY_3_TARGET_SHA, + package_coordinate=GOVERNANCE_RETRY_3_ACCEPTANCE_PACKAGE_COORDINATE, + confirmation_digest=( + "sha256:" + "33e59948941f5f1111d5017ab80dd33c90dd2ac8d1a17203e7f7382a8c5b2c72" + ), + environment=GOVERNANCE_RETRY_3_ACCEPTANCE_ENVIRONMENT, + scenario_coordinates=TEST_LOCAL_RETRY_3_SCENARIO_COORDINATES, + proof_namespace="historical-retry-3", + ), + } + + for profile_name, document in documents.items(): + admitted = _admit(document) + admitted_document = admitted.to_document() + scenarios = { + scenario["scenario"]: scenario + for fact in admitted_document["probe-facts"] + for scenario in fact["scenarios"] + } + + assert admitted_document == document + assert canonicalize(admitted_document) == canonicalize(document) + assert admitted.evidence_digest == canonical_sha256(document) + assert ( + tuple( + fact["record-digest"] + for fact in admitted_document["probe-facts"] + ) + == TEST_LOCAL_HISTORICAL_FINALIZED_SUITE_DIGESTS[profile_name] + ) + assert tuple(scenarios) == GOVERNANCE_ACCEPTANCE_SCENARIOS + for scenario_name, scenario_document in scenarios.items(): + expected = historical_profile_tuples[ + ("retry-1", "retry-2", "retry-3").index(profile_name) + ][5][GOVERNANCE_ACCEPTANCE_SCENARIOS.index(scenario_name)] + assert ( + scenario_document["scenario"], + scenario_document["package-coordinate"], + scenario_document["tag"], + ) == expected + + retry_1_scenarios = { + scenario["scenario"]: scenario + for fact in documents["retry-1"]["probe-facts"] + for scenario in fact["scenarios"] + } + assert ( + "validated-request-proof" + not in retry_1_scenarios["absent-create-readback"] + ) + for profile_name in ("retry-2", "retry-3"): + scenarios = { + scenario["scenario"]: scenario + for fact in documents[profile_name]["probe-facts"] + for scenario in fact["scenarios"] + } + for scenario_name in ("absent-create-readback", "lost-response"): + scenario_document = scenarios[scenario_name] + proof = scenario_document["validated-request-proof"] + assert ( + scenario_document["response"]["identity-digest"] + == proof["response-identity-digest"] + ) + assert ( + scenario_document["post"]["content-sha512"] + == proof["tarball-sha512"] + )