From d2684f30f28e11f5f04a9588df27a1216f815727 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 16:37:12 +0900 Subject: [PATCH] docs(evidence): link canonical merge collector --- AGENTS.md | 1 + CHANGELOG.md | 2 + CLAUDE.md | 1 + docs/product-technical-gap-baseline.md | 168 +----------------- tests/test_product_completion_gap_contract.py | 51 +++--- 5 files changed, 33 insertions(+), 190 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 81425046e..67837d1fa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -131,6 +131,7 @@ A release requires all current-head checks, complete coverage and docs, updated - When updating a delivery checkpoint, separate a verified predecessor from a newer pending head. A passing coverage summary does not validate a fixture that ignores peer errors; preserve the failing reproduction and the repaired wire-level assertions in the evidence trail. - A live-inventory contract must update its dated baseline, `CHANGELOG.md`, and full exact SHA together. Use `scripts/ci/collect_live_merge_evidence.sh` for reusable head/base evidence; do not infer current state from an abbreviated SHA or a historical inventory line. +- Keep the delivery baseline decision-sized: GitHub truncates large Markdown code blocks. Link the canonical executable evidence collector instead of copying it into the rendered baseline, and keep its contract test pointed at that executable source. - Add concise, reproducible lessons here as work establishes them. Keep transient heads, job IDs and incident snapshots in PR evidence, not permanent instructions; never record secret values. - Retained receipt recovery is not live-stream recovery. State whether a fixture keeps the original connection open and uses the same endpoint; claim live recovery only when a synchronized test reads and completes the original request after rejecting the replacement reply. diff --git a/CHANGELOG.md b/CHANGELOG.md index 84207cb33..23635db9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to OriginWeave are documented in this file. The format follo ## [Unreleased] +- Replaced the truncated inline merge-evidence command copy with its canonical executable collector and kept the collector's exact-head contract under test. + - Recorded #255 exact-head hosted success and #293's narrow standard-BiDi capability, typed command planning, dated-TR provenance pin, and explicit viewport/DPR cleanup intent, preserving exact-head visual, rustdoc-view, transport, and real-browser evidence gaps. - Recorded the server Close-role RED-to-GREEN repair with exact local coverage and visual evidence, keeping queued hosted checks and release acceptance separate. diff --git a/CLAUDE.md b/CLAUDE.md index cbeb8220b..1c762e5ca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,3 +13,4 @@ Additional constraints: - Keep changes bounded to one product gap and preserve modular crate boundaries. - Never claim a test, benchmark, browser integration, TLS identity, GPU execution, release, or merge succeeded without current exact-head evidence. - When refreshing live delivery evidence, update the dated baseline, `CHANGELOG.md`, and full exact SHA atomically; use `scripts/ci/collect_live_merge_evidence.sh` rather than an abbreviated SHA or historical count. +- Keep rendered delivery evidence concise: link the canonical collector rather than embedding its long shell body, because GitHub truncates oversized code blocks. diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 315696654..f1b462277 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1252,173 +1252,7 @@ OriginWeave is not complete merely because every low-level primitive exists in s Run `scripts/ci/collect_live_merge_evidence.sh` to collect reusable, exact-head and exact-base merge evidence before interpreting the volatile inventory below. -The volatile counts above are reproducible by paginating the complete open-PR and open-issue inventories, excluding pull requests from the issue count, flattening every page, and then inspecting each PR's exact head, checks, reviews, and review threads: - -```bash -set -euo pipefail -EVIDENCE_DIR="$(mktemp -d /tmp/originweave-evidence.XXXXXX)" -printf 'Evidence directory: %s\n' "$EVIDENCE_DIR" >&2 - -gh api --paginate --slurp 'repos/ContextualWisdomLab/OriginWeave/pulls?state=open&per_page=100' \ - > "$EVIDENCE_DIR/open-pr-pages.json" -jq '[.[][]]' "$EVIDENCE_DIR/open-pr-pages.json" \ - > "$EVIDENCE_DIR/open-prs.json" -jq '{ - open_pull_requests: length, - non_draft: (map(select(.draft == false)) | length), - draft: (map(select(.draft == true)) | length) -}' "$EVIDENCE_DIR/open-prs.json" - -gh api --paginate --slurp 'repos/ContextualWisdomLab/OriginWeave/issues?state=open&per_page=100' \ - > "$EVIDENCE_DIR/open-issue-pages.json" -jq '[.[][]] | map(select(has("pull_request") | not)) | { - open_non_pr_issues: length -}' "$EVIDENCE_DIR/open-issue-pages.json" - -gh api 'repos/ContextualWisdomLab/OriginWeave/branches/main' \ - > "$EVIDENCE_DIR/main-branch.json" -gh api --paginate --slurp \ - 'repos/ContextualWisdomLab/OriginWeave/rules/branches/main?per_page=100' \ - > "$EVIDENCE_DIR/main-branch-rule-pages.json" -jq '[.[][]]' "$EVIDENCE_DIR/main-branch-rule-pages.json" \ - > "$EVIDENCE_DIR/main-branch-rules.json" -gh api --paginate --slurp \ - 'repos/ContextualWisdomLab/OriginWeave/collaborators?affiliation=all&per_page=100' \ - > "$EVIDENCE_DIR/collaborator-pages.json" -jq '[.[][]]' "$EVIDENCE_DIR/collaborator-pages.json" \ - > "$EVIDENCE_DIR/collaborators.json" - -jq -r '.[].number' "$EVIDENCE_DIR/open-prs.json" | while read -r PR; do - STABLE_HEAD=false - for ATTEMPT in 1 2 3; do - VERDICT_PATH="$EVIDENCE_DIR/pr-${PR}-merge-verdict.json" - VERDICT_TMP="$EVIDENCE_DIR/pr-${PR}-merge-verdict.json.tmp" - rm -f "$VERDICT_PATH" "$VERDICT_TMP" "$EVIDENCE_DIR/pr-${PR}-rechecked.json" - PR_JSON="$EVIDENCE_DIR/pr-${PR}.json" - gh api "repos/ContextualWisdomLab/OriginWeave/pulls/$PR" > "$PR_JSON" - HEAD_SHA=$(jq -r '.head.sha' "$PR_JSON") - BASE_SHA=$(jq -r '.base.sha' "$PR_JSON") - - gh api --paginate --slurp \ - "repos/ContextualWisdomLab/OriginWeave/commits/$HEAD_SHA/check-runs?per_page=100" \ - > "$EVIDENCE_DIR/pr-${PR}-check-runs.json" - gh api --paginate --slurp \ - "repos/ContextualWisdomLab/OriginWeave/commits/$HEAD_SHA/statuses?per_page=100" \ - > "$EVIDENCE_DIR/pr-${PR}-statuses.json" - gh api --paginate --slurp \ - "repos/ContextualWisdomLab/OriginWeave/pulls/$PR/reviews?per_page=100" \ - > "$EVIDENCE_DIR/pr-${PR}-reviews.json" - gh api --paginate --slurp \ - "repos/ContextualWisdomLab/OriginWeave/actions/runs?head_sha=$HEAD_SHA&per_page=100" \ - > "$EVIDENCE_DIR/pr-${PR}-workflow-runs.json" - gh api graphql --paginate --slurp \ - -F owner=ContextualWisdomLab \ - -F name=OriginWeave \ - -F number="$PR" \ - -f query=' -query($owner: String!, $name: String!, $number: Int!, $endCursor: String) { - repository(owner: $owner, name: $name) { - pullRequest(number: $number) { - reviewThreads(first: 100, after: $endCursor) { - nodes { id isResolved isOutdated } - pageInfo { hasNextPage endCursor } - } - } - } -}' > "$EVIDENCE_DIR/pr-${PR}-review-threads.json" - - jq -n \ - --arg head "$HEAD_SHA" \ - --slurpfile pr "$PR_JSON" \ - --slurpfile checks "$EVIDENCE_DIR/pr-${PR}-check-runs.json" \ - --slurpfile statuses "$EVIDENCE_DIR/pr-${PR}-statuses.json" \ - --slurpfile reviews "$EVIDENCE_DIR/pr-${PR}-reviews.json" \ - --slurpfile workflow_runs "$EVIDENCE_DIR/pr-${PR}-workflow-runs.json" \ - --slurpfile rules "$EVIDENCE_DIR/main-branch-rules.json" \ - --slurpfile collaborators "$EVIDENCE_DIR/collaborators.json" \ - --slurpfile threads "$EVIDENCE_DIR/pr-${PR}-review-threads.json" \ - --arg base "$BASE_SHA" \ - '( - [ - $rules[][]? - | select(.type == "pull_request") - | .parameters - ] | first // {} - ) as $pull_request_parameters - | ( - [ - $reviews[][][]? - | {reviewer: .user.login, state, submitted_at, commit_id} - | select(.submitted_at != null) - | select(.reviewer != $pr[0].user.login) - | select(.reviewer as $reviewer | - any($collaborators[][]?; - .login == $reviewer and - (.permissions.push == true or - .permissions.maintain == true or - .permissions.admin == true))) - ] - | group_by(.reviewer) - | map(sort_by(.submitted_at) | last) - | map(select(.state == "APPROVED" and .commit_id == $head)) - ) as $current_approvals - | ($pull_request_parameters.required_approving_review_count // 0) as $required_review_count - | ($pull_request_parameters.require_last_push_approval // false) as $require_last_push_approval - | { - head_sha: $head, - base_sha: $base, - required_status_checks: { - check_runs: [$checks[][].check_runs[]?], - legacy_statuses: [$statuses[][][]?] - }, - workflow_runs: [$workflow_runs[][].workflow_runs[]?], - counted_approvals: ($current_approvals | length), - required_approving_review_count: $required_review_count, - require_last_push_approval: $require_last_push_approval, - last_push_approval_authority: ( - if $require_last_push_approval == true - then "github_rule_evaluation_required" - else "not_required" - end - ), - approval_gate_satisfied: ( - if $pull_request_parameters.require_last_push_approval == true then false - else (($current_approvals | length) >= $required_review_count) - end - ), - required_workflows: [ - $rules[][]? - | select(.type == "workflows") - | .parameters.workflows[] - ], - unresolved_threads: [ - $threads[][].data.repository.pullRequest.reviewThreads.nodes[]? - | select(.isResolved == false and .isOutdated == false) - ] - }' > "$VERDICT_TMP" - - RECHECKED_PR_JSON="$EVIDENCE_DIR/pr-${PR}-rechecked.json" - RECHECKED_HEAD_SHA=$(gh api "repos/ContextualWisdomLab/OriginWeave/pulls/$PR" \ - | tee "$RECHECKED_PR_JSON" \ - | jq -r '.head.sha') - RECHECKED_BASE_SHA=$(jq -r '.base.sha' "$RECHECKED_PR_JSON") - if [[ "$RECHECKED_HEAD_SHA" == "$HEAD_SHA" && "$RECHECKED_BASE_SHA" == "$BASE_SHA" ]]; then - mv "$VERDICT_TMP" "$VERDICT_PATH" - mv "$RECHECKED_PR_JSON" "$PR_JSON" - STABLE_HEAD=true - break - fi - rm -f "$VERDICT_TMP" "$RECHECKED_PR_JSON" - printf 'Discarding moving head/base evidence for PR #%s (head %s -> %s, base %s -> %s) and retrying.\n' \ - "$PR" "$HEAD_SHA" "$RECHECKED_HEAD_SHA" "$BASE_SHA" "$RECHECKED_BASE_SHA" >&2 - done - if [[ "$STABLE_HEAD" != true ]]; then - rm -f "$EVIDENCE_DIR"/pr-${PR}-*.json - printf 'Unable to collect stable exact-head/base evidence for PR #%s after 3 attempts.\n' "$PR" >&2 - exit 1 - fi -done -``` +The executable [collect_live_merge_evidence.sh](../scripts/ci/collect_live_merge_evidence.sh) procedure paginates the complete open-PR and open-issue inventories, excludes pull requests from the issue count, and binds each merge verdict to the exact head, base, checks, reviews, and review threads. It is the canonical procedure; do not copy it into this baseline. The branch-scoped rules response determines the active rules affecting `main`; each PR's exact `HEAD_SHA` then determines which check runs, legacy statuses, workflow runs, reviews, and unresolved threads are current. The saved merge verdict binds counted approvals to the latest review per eligible collaborator, excludes the PR author, and requires `APPROVED` on the exact head. It deliberately does **not** infer GitHub's actual last-push actor from commit author or committer metadata: when `require_last_push_approval` is active, this portable evidence procedure records `github_rule_evaluation_required` and keeps `approval_gate_satisfied` false until GitHub's authoritative rule evaluation is consulted. The saved PR JSON also preserves the exact base reference and branch ancestry input for the dependency graph. Evidence is retained only when both `RECHECKED_HEAD_SHA` and `RECHECKED_BASE_SHA` match the collected values; a moving head or base discards the temporary verdict, and three failed attempts leave no unstable merge verdict. diff --git a/tests/test_product_completion_gap_contract.py b/tests/test_product_completion_gap_contract.py index 9cd70908a..2485d3f3b 100644 --- a/tests/test_product_completion_gap_contract.py +++ b/tests/test_product_completion_gap_contract.py @@ -719,29 +719,34 @@ def test_issue_table_distinguishes_open_issues_from_governance_signals(self) -> self.assertIn("Issue or signal", table) def test_evidence_commands_reproduce_inventory_checks_and_review_state(self) -> None: - """The evidence procedure must paginate the queue and inspect each exact PR head.""" + """The baseline links to the executable procedure without duplicating it.""" text = BASELINE.read_text(encoding="utf-8") evidence = text.split("## Evidence commands", 1)[1].split("\n## ", 1)[0] - shell = evidence.split("```bash", 1)[1].split("```", 1)[0] + script = (ROOT / "scripts" / "ci" / "collect_live_merge_evidence.sh").read_text( + encoding="utf-8" + ) + + self.assertIn("[collect_live_merge_evidence.sh]", evidence) + self.assertNotIn("```bash", evidence) for phrase in ( - "--paginate --slurp 'repos/ContextualWisdomLab/OriginWeave/pulls?state=open&per_page=100'", + '"repos/$REPOSITORY/pulls?state=open&per_page=100"', "set -euo pipefail", 'EVIDENCE_DIR="$(mktemp -d /tmp/originweave-evidence.XXXXXX)"', '"$EVIDENCE_DIR/open-pr-pages.json"', "jq '[.[][]]' \"$EVIDENCE_DIR/open-pr-pages.json\"", - "--paginate --slurp 'repos/ContextualWisdomLab/OriginWeave/issues?state=open&per_page=100'", + '"repos/$REPOSITORY/issues?state=open&per_page=100"', '"$EVIDENCE_DIR/open-issue-pages.json"', 'map(select(has("pull_request") | not))', "open_non_pr_issues", - '"repos/ContextualWisdomLab/OriginWeave/pulls/$PR"', - '"repos/ContextualWisdomLab/OriginWeave/commits/$HEAD_SHA/check-runs?per_page=100"', - '"repos/ContextualWisdomLab/OriginWeave/commits/$HEAD_SHA/statuses?per_page=100"', - '"repos/ContextualWisdomLab/OriginWeave/pulls/$PR/reviews?per_page=100"', - '"repos/ContextualWisdomLab/OriginWeave/actions/runs?head_sha=$HEAD_SHA&per_page=100"', + '"repos/$REPOSITORY/pulls/$PR"', + '"repos/$REPOSITORY/commits/$HEAD_SHA/check-runs?per_page=100"', + '"repos/$REPOSITORY/commits/$HEAD_SHA/statuses?per_page=100"', + '"repos/$REPOSITORY/pulls/$PR/reviews?per_page=100"', + '"repos/$REPOSITORY/actions/runs?head_sha=$HEAD_SHA&per_page=100"', "check_runs: [$checks[][].check_runs[]?],", "legacy_statuses: [$statuses[][][]?]", - "workflow_runs: [$workflow_runs[][].workflow_runs[]?],", + "workflow_runs: $exact_pr_base_workflow_runs,", "reviewThreads(first: 100, after: $endCursor)", "rules/branches/main?per_page=100", '"$EVIDENCE_DIR/main-branch-rule-pages.json"', @@ -756,7 +761,7 @@ def test_evidence_commands_reproduce_inventory_checks_and_review_state(self) -> "require_last_push_approval", "last_push_approval_authority", '"github_rule_evaluation_required"', - "if $pull_request_parameters.require_last_push_approval == true then false", + "if $require_last_push_approval == true then false", "$pr[0].user.login", '.type == "workflows"', ".parameters.workflows", @@ -765,20 +770,20 @@ def test_evidence_commands_reproduce_inventory_checks_and_review_state(self) -> "for ATTEMPT in 1 2 3; do", "RECHECKED_HEAD_SHA=", "RECHECKED_BASE_SHA=", - 'if [[ "$RECHECKED_HEAD_SHA" == "$HEAD_SHA" && "$RECHECKED_BASE_SHA" == "$BASE_SHA" ]]; then', + '"$RECHECKED_BASE_SHA" == "$BASE_SHA" &&', ): with self.subTest(phrase=phrase): - self.assertIn(phrase, shell) - - self.assertNotIn("while :; do", shell) - self.assertNotIn("/tmp/originweave-open-pr", shell) - self.assertNotIn("check_runs: [$checks[]?.check_runs[]?],", shell) - self.assertNotIn("legacy_statuses: [$statuses[][]?]", shell) - self.assertNotIn("workflow_runs: [$workflow_runs[]?.workflow_runs[]?],", shell) - self.assertNotIn("$reviews[][]?\n | select(.state", shell) - self.assertNotIn("head-commit.json", shell) - self.assertNotIn("$head_commit[0].committer.login", shell) - self.assertNotIn("$head_commit[0].author.login", shell) + self.assertIn(phrase, script) + + self.assertNotIn("while :; do", script) + self.assertNotIn("/tmp/originweave-open-pr", script) + self.assertNotIn("check_runs: [$checks[]?.check_runs[]?],", script) + self.assertNotIn("legacy_statuses: [$statuses[][]?]", script) + self.assertNotIn("workflow_runs: [$workflow_runs[]?.workflow_runs[]?],", script) + self.assertNotIn("$reviews[][]?\n | select(.state", script) + self.assertNotIn("head-commit.json", script) + self.assertNotIn("$head_commit[0].committer.login", script) + self.assertNotIn("$head_commit[0].author.login", script) if __name__ == "__main__":