diff --git a/.github/workflows/check-vsm-oss-metrics.yml b/.github/workflows/check-vsm-oss-metrics.yml index 4f40f9e..8693aa3 100644 --- a/.github/workflows/check-vsm-oss-metrics.yml +++ b/.github/workflows/check-vsm-oss-metrics.yml @@ -102,16 +102,37 @@ jobs: - name: Materialize canonical Index corpus metrics locally run: node scripts/sync-vsm-metrics.cjs --source .upstream/vsm-harness-index + - name: Revalidate Upstream accepted merged changes via first-party GitHub API + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + set -u + if python .upstream/upstream/scripts/verified_external_outcomes.py --report --output-json .upstream/upstream-observed.json && + python .upstream/upstream/scripts/accepted_external_outcomes.py --observation-json .upstream/upstream-observed.json --output-json .upstream/upstream-accepted.json >/dev/null; then + echo "Source-owned first-party Upstream evidence validated." + else + rm -f .upstream/upstream-observed.json .upstream/upstream-accepted.json + echo "::warning::Live external evidence unavailable; outcome metric stays unclaimed." + fi + - name: Collect canonical public VSM OSS snapshot - run: >- - python .upstream/vsm-oss-organization/scripts/collect_public_metrics.py - --profile-root .upstream/vsm-harness-profile - --skills-root .upstream/vsm-harness-skills - --index-root .upstream/vsm-harness-index - --awesome-root .upstream/awesome-vsm-harness - --upstream-root .upstream/upstream - --organization-root .upstream/vsm-oss-organization - --output .upstream/vsm-oss-metrics.json + shell: bash + run: | + set -euo pipefail + accepted=() + if [ -f .upstream/upstream-accepted.json ]; then + accepted+=(--upstream-accepted .upstream/upstream-accepted.json) + fi + python .upstream/vsm-oss-organization/scripts/collect_public_metrics.py \ + --profile-root .upstream/vsm-harness-profile \ + --skills-root .upstream/vsm-harness-skills \ + --index-root .upstream/vsm-harness-index \ + --awesome-root .upstream/awesome-vsm-harness \ + --upstream-root .upstream/upstream \ + --organization-root .upstream/vsm-oss-organization \ + --output .upstream/vsm-oss-metrics.json \ + "${accepted[@]}" - name: Finalize canonical historical windows run: >- diff --git a/.github/workflows/sync-vsm-index.yml b/.github/workflows/sync-vsm-index.yml index a9da558..0c9883c 100644 --- a/.github/workflows/sync-vsm-index.yml +++ b/.github/workflows/sync-vsm-index.yml @@ -154,17 +154,39 @@ jobs: if: steps.preflight.outputs.skip != 'true' run: node scripts/sync-vsm-metrics.cjs --source .upstream/vsm-harness-index + - name: Revalidate Upstream accepted merged changes via first-party GitHub API + if: steps.preflight.outputs.skip != 'true' + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + set -u + if python .upstream/upstream/scripts/verified_external_outcomes.py --report --output-json .upstream/upstream-observed.json && + python .upstream/upstream/scripts/accepted_external_outcomes.py --observation-json .upstream/upstream-observed.json --output-json .upstream/upstream-accepted.json >/dev/null; then + echo "Source-owned first-party Upstream evidence validated." + else + rm -f .upstream/upstream-observed.json .upstream/upstream-accepted.json + echo "::warning::Live external evidence unavailable; outcome metric stays unclaimed." + fi + - name: Collect canonical public VSM OSS snapshot if: steps.preflight.outputs.skip != 'true' - run: >- - python .upstream/vsm-oss-organization/scripts/collect_public_metrics.py - --profile-root .upstream/vsm-harness-profile - --skills-root .upstream/vsm-harness-skills - --index-root .upstream/vsm-harness-index - --awesome-root .upstream/awesome-vsm-harness - --upstream-root .upstream/upstream - --organization-root .upstream/vsm-oss-organization - --output .upstream/vsm-oss-metrics.json + shell: bash + run: | + set -euo pipefail + accepted=() + if [ -f .upstream/upstream-accepted.json ]; then + accepted+=(--upstream-accepted .upstream/upstream-accepted.json) + fi + python .upstream/vsm-oss-organization/scripts/collect_public_metrics.py \ + --profile-root .upstream/vsm-harness-profile \ + --skills-root .upstream/vsm-harness-skills \ + --index-root .upstream/vsm-harness-index \ + --awesome-root .upstream/awesome-vsm-harness \ + --upstream-root .upstream/upstream \ + --organization-root .upstream/vsm-oss-organization \ + --output .upstream/vsm-oss-metrics.json \ + "${accepted[@]}" - name: Finalize VSM OSS historical windows if: steps.preflight.outputs.skip != 'true' diff --git a/scripts/sync-vsm-oss-metrics.cjs b/scripts/sync-vsm-oss-metrics.cjs index ab409c6..71790ca 100644 --- a/scripts/sync-vsm-oss-metrics.cjs +++ b/scripts/sync-vsm-oss-metrics.cjs @@ -79,6 +79,50 @@ if (snapshot.publication.engineering_activity_is_productivity_kpi !== false) { throw new Error('Engineering activity must remain non-KPI'); } +function verifiedUpstreamEvidence(snapshot) { + const owner = snapshot.repositories['opensiro/upstream']; + const outcome = owner && owner.metrics && owner.metrics.verified_external_outcomes; + if (!outcome || outcome.value == null) return ''; + const records = outcome.accepted; + if (!Number.isSafeInteger(outcome.value) || outcome.value < 0 + || outcome.claim_status !== 'verified_merged_changes_only_not_deployed_benefit' + || outcome.completion_gate_evaluated !== true + || !Array.isArray(records) || records.length !== outcome.value + || typeof outcome.observed_at !== 'string' + || !/^(?:20\d\d)-\d\d-\d\dT/.test(outcome.observed_at) + || !/^[a-f0-9]{64}$/.test(outcome.owner_source_sha256 || '') + || !/^[a-f0-9]{64}$/.test(outcome.portfolio_cohort_sha256 || '')) { + throw new Error('Invalid source-verified Upstream outcome contract'); + } + const seenIds = new Set(); + const seenPrs = new Set(); + const links = records.map((record) => { + if (!record || typeof record.intervention_id !== 'string' || !record.intervention_id + || !/^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9]\d*$/.test(record.external_pr_url || '') + || !/^https:\/\/github\.com\/opensiro\/upstream\/(?:actions\/runs\/[1-9]\d*|blob\/[a-f0-9]{40}\/[^?#]+)$/.test(record.native_receipt || '') + || !/^[a-f0-9]{40}$/.test(record.merge_commit_sha || '') + || record.decision !== 'ACCEPTED_MERGED_CHANGE_ONLY' + || record.acceptance_basis !== 'DETERMINISTIC_VERIFIED_MERGE' + || record.benefit_claim !== 'NOT_MEASURED' + || seenIds.has(record.intervention_id) + || seenPrs.has(record.external_pr_url.toLowerCase())) { + throw new Error('Invalid or duplicated Upstream merged-change evidence'); + } + seenIds.add(record.intervention_id); + seenPrs.add(record.external_pr_url.toLowerCase()); + const short = record.external_pr_url.replace('https://github.com/', ''); + return '
Accepted contributor merges only, not deployment or measured user benefit. ' + + 'Observed ' + escapeHtml(outcome.observed_at) + '.
' + + 'Repository-owned state and net canonical change. Updated ${escapeHtml(collected)}.
Window values are net canonical-state change, not gross event counts. Unlike outputs are never summed into a productivity score.
+ ${outcomeEvidence}