Skip to content
Merged

Test #643

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
33a6326
fix(server): honor exclude on the mcp kb_export tool
Yurii214 Jul 30, 2026
8fe527a
fix(worthiness): preserve a configured min_score of 0
Yurii214 Jul 30, 2026
d84f633
test: cover the cli, mcp, jsonl and embeddings surfaces
plind-junior Jul 30, 2026
ad7627e
ci(coverage): gate prs on 100% diff coverage and report uncovered lines
plind-junior Jul 30, 2026
2951402
ci(workflow-lint): quiet shellcheck in the diff-coverage comment
plind-junior Jul 30, 2026
44b791a
Merge pull request #619 from vouchdev/ci/diff-coverage-gate
plind-junior Jul 30, 2026
9852801
Merge branch 'test' into fix/worthiness-min-score-zero
plind-junior Jul 30, 2026
80c32b5
Merge pull request #603 from Yurii214/fix/worthiness-min-score-zero
plind-junior Jul 30, 2026
1c89625
fix(salience): exclude retracted claims from the reflex sidebar
minion1227 Jul 30, 2026
6894a5e
ci(auto-merge): gate core prs on coverage plus an owner issue
plind-junior Jul 30, 2026
f619ba0
ci(auto-merge): quiet SC2016 on the closing-issue graphql query
plind-junior Jul 30, 2026
870a375
feat(server): kb.explain_ranking — why a result ranked where it did
minion1227 Jul 30, 2026
e9483db
ci(coderabbit): drop the coderabbit approval gate
plind-junior Jul 30, 2026
1b61278
Merge pull request #629 from vouchdev/ci/drop-coderabbit-gate
plind-junior Jul 30, 2026
01f3f18
Merge branch 'test' into fix/salience-retracted-claims
plind-junior Jul 30, 2026
08d7a29
Merge branch 'test' into ci/auto-merge-core-gate
plind-junior Jul 30, 2026
42690fd
fix(config): coerce quoted false for enrich, events, pages_first (#621)
kurosawareiji7007-hub Jul 30, 2026
49c5ac1
Merge branch 'test' into ci/auto-merge-core-gate
plind-junior Jul 30, 2026
a1b6eb0
Merge pull request #627 from vouchdev/ci/auto-merge-core-gate
plind-junior Jul 30, 2026
73ee7d1
Merge branch 'test' into fix/salience-retracted-claims
plind-junior Jul 30, 2026
4875583
Merge pull request #624 from minion1227/fix/salience-retracted-claims
plind-junior Jul 30, 2026
a155578
Merge branch 'test' into feat/explain-ranking
plind-junior Jul 30, 2026
ccac093
ci(trust-gate): drop the trust gate
plind-junior Jul 30, 2026
600ec43
Merge pull request #630 from vouchdev/ci/drop-trust-gate
plind-junior Jul 30, 2026
5901823
Merge branch 'test' into feat/explain-ranking
plind-junior Jul 30, 2026
d250260
fix(verify/doctor): count missing externals as failures (#622)
kurosawareiji7007-hub Jul 30, 2026
58beba0
Merge pull request #628 from minion1227/feat/explain-ranking
plind-junior Jul 30, 2026
98d9759
Merge branch 'test' into fix/kb-export-mcp-exclude
plind-junior Jul 30, 2026
43e2edd
Merge pull request #618 from Yurii214/fix/kb-export-mcp-exclude
plind-junior Jul 30, 2026
a17d904
Merge branch 'test' into fix/verify-missing-external
plind-junior Jul 30, 2026
b03a1ce
Merge pull request #623 from kurosawareiji7007-hub/fix/verify-missing…
plind-junior Jul 30, 2026
36c2270
fix(digest): drop archived pages from followups-due (#625)
kurosawareiji7007-hub Jul 30, 2026
8f49088
ci(auto-merge): arm auto-merge unattended when ci goes green
plind-junior Jul 30, 2026
f24243d
Merge pull request #642 from vouchdev/ci/auto-merge-on-green
plind-junior Jul 30, 2026
a84a6ff
Merge branch 'test' into fix/digest-archived-followups
plind-junior Jul 30, 2026
ae573d2
Merge pull request #626 from kurosawareiji7007-hub/fix/digest-archive…
plind-junior Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# CodeRabbit configuration — https://docs.coderabbit.ai/guides/configure-coderabbit
# CodeRabbit is the required review gate for vouch (free for this public repo). it
# reviews every non-draft PR automatically. request_changes_workflow is on, so it
# submits a formal approve / request-changes review; the coderabbit-gate workflow
# turns that verdict into the required `coderabbit-approved` status check, so a pr
# only auto-merges once CodeRabbit approves (on top of ci + trust-gate + CODEOWNERS,
# with the owner's auto-merge label as the go signal). a pr CodeRabbit requests
# changes on 3 times is auto-closed (the owner and bots are exempt).
# CodeRabbit reviews every non-draft PR automatically (free for this public repo).
# its verdict is advisory: it gates nothing and closes nothing. the merge path is
# ci + CODEOWNERS, with the owner's auto-merge label as the go signal.
# request_changes_workflow stays on so its stance is legible at a glance, but a
# request-changes review no longer blocks or reaps a pr.
language: "en-US"
early_access: false
reviews:
Expand Down
115 changes: 115 additions & 0 deletions .github/workflows/arm-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: arm-auto-merge
# the single arming path, called by both authorization surfaces:
# auto-merge.yml (the `auto-merge` label) and comment-command.yml (`/auto-merge`).
# both callers have already established that the actor is the trusted owner —
# this workflow decides whether the PR has earned an unattended merge.
#
# two bars, both read as metadata. nothing here checks out or executes PR code,
# because this job holds a write token.
#
# 1. every changed python line under src/vouch/ is executed by a test
# (the `diff coverage` check, green on this exact head sha).
# 2. the PR closes an issue that plind-junior opened.
#
# together they replace the old blanket refusal to arm `core` PRs: coverage
# says the change is exercised, the issue link says it was asked for.
on:
workflow_call:
inputs:
pr:
description: the pull request number
required: true
type: string
head_sha:
description: >-
head sha to read checks from. pass the sha carried by the
authorizing event where one exists; empty resolves it live.
required: false
default: ""
type: string
permissions: {}
jobs:
arm:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: read
steps:
- name: resolve the head sha
id: head
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr }}
GIVEN: ${{ inputs.head_sha }}
run: |
sha="$GIVEN"
if [ -z "$sha" ]; then
sha="$(gh pr view "$PR" --repo "$REPO" --json headRefOid --jq .headRefOid)"
fi
echo "sha=$sha" >> "$GITHUB_OUTPUT"

# the coverage bar, read from ci's own run — never recomputed here, because
# that would mean executing PR code in a workflow that holds a write token.
- name: require the diff-coverage check to have passed
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr }}
HEAD_SHA: ${{ steps.head.outputs.sha }}
run: |
conclusion=$(gh api "repos/$REPO/commits/$HEAD_SHA/check-runs" --paginate \
--jq '[.check_runs[] | select(.name | startswith("diff coverage"))]
| sort_by(.completed_at) | last | .conclusion' 2>/dev/null || true)
if [ "$conclusion" = "success" ]; then
exit 0
fi
echo "::error::diff coverage is not green on $HEAD_SHA (conclusion=${conclusion:-missing}); refusing to arm auto-merge"
gh pr edit "$PR" --repo "$REPO" --remove-label auto-merge || true
gh pr comment "$PR" --repo "$REPO" --body \
"auto-merge was not armed: the \`diff coverage\` check is not green on this head. every python line this PR changes under \`src/vouch/\` must be executed by a test. the bot has commented the uncovered lines; push tests and re-add the auto-merge label."
exit 1

# closingIssuesReferences is the resolved link github itself computes from
# `fixes #n` / `closes #n` in the body and commits — not a text match, so a
# bare "#123" mention does not qualify. the issue must be the owner's: an
# unattended merge answers work plind-junior asked for, nothing else.
- name: require a closing issue opened by the owner
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr }}
run: |
# SC2016: the query is single-quoted on purpose — $owner/$name/$pr are
# graphql variables bound by -f/-F, not shell expansions.
# shellcheck disable=SC2016
owners=$(gh api graphql \
-f owner="${REPO%/*}" -f name="${REPO#*/}" -F pr="$PR" \
-f query='query($owner:String!,$name:String!,$pr:Int!){
repository(owner:$owner,name:$name){
pullRequest(number:$pr){
closingIssuesReferences(first:50){nodes{number author{login}}}
}
}
}' \
--jq '[.data.repository.pullRequest.closingIssuesReferences.nodes[]
| select(.author.login=="plind-junior") | .number] | join(", ")' \
2>/dev/null || true)
if [ -n "$owners" ]; then
echo "closes owner-authored issue(s): $owners"
exit 0
fi
echo "::error::no closing reference to an issue opened by plind-junior; refusing to arm auto-merge"
gh pr edit "$PR" --repo "$REPO" --remove-label auto-merge || true
gh pr comment "$PR" --repo "$REPO" --body \
"auto-merge was not armed: this PR does not close an issue opened by plind-junior. add a \`fixes #<issue>\` line to the PR body pointing at the owner's ticket, then re-add the auto-merge label. a bare \`#<issue>\` mention is not a closing reference."
exit 1

- name: arm native auto-merge
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr }}
run: |
gh pr merge "$PR" --repo "$REPO" --auto --squash
44 changes: 10 additions & 34 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ jobs:
permissions:
contents: read
pull-requests: write
outputs:
klass: ${{ steps.classify.outputs.klass }}
steps:
- name: the labeler must be the trusted owner (fail closed)
env:
Expand All @@ -54,40 +52,18 @@ jobs:
gh pr edit "$PR" --repo "$REPO" --remove-label auto-merge || true
exit 1
fi
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ github.event.pull_request.base.sha }}
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: classify
id: classify
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
# REST files endpoint carries previous_filename on renames; the
# GraphQL-backed `gh pr view --json files` shortcut does not.
gh api "repos/$REPO/pulls/$PR/files" --paginate > files.json
PYTHONPATH=src python -m vouch.pr_bot changed-files --json-file files.json > changed.txt
klass=$(PYTHONPATH=src python -m vouch.pr_bot classify --files-file changed.txt --print-klass)
echo "klass=$klass" >> "$GITHUB_OUTPUT"

# core PRs are no longer refused outright. arm-auto-merge decides, on the same
# two bars for every klass: full diff coverage, and a closing reference to an
# issue plind-junior opened.
arm:
needs: guard
# core PRs are never armed — CODEOWNERS requires the owner's approval.
if: needs.guard.outputs.klass != 'core'
runs-on: ubuntu-latest
# a called workflow can only downgrade the caller's token, and this file
# starts from `permissions: {}` — so the grant has to be made here too.
permissions:
contents: write
pull-requests: write
steps:
- name: arm native auto-merge (non-core)
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
gh pr merge "$PR" --repo "$REPO" --auto --squash
checks: read
uses: ./.github/workflows/arm-auto-merge.yml
with:
pr: ${{ github.event.pull_request.number }}
head_sha: ${{ github.event.pull_request.head.sha }}
163 changes: 163 additions & 0 deletions .github/workflows/ci-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: ci-auto-merge
# the unattended arming path: no human acts, the machine decides.
#
# when `ci` finishes green for a pull request, wait for every other check on
# that exact head sha to finish too, and if none of them failed, arm native
# auto-merge through arm-auto-merge.yml. the two bars there are unchanged and
# unweakened by this path: 100% diff coverage of the changed python under
# src/vouch/, and a closing reference to an issue plind-junior opened. a PR
# that clears neither of them is refused here exactly as on the label path.
#
# so an unattended merge needs all three, and the machine checks all three:
# every check green, every changed line tested, and the work was asked for by
# the owner.
#
# nothing here checks out or executes PR code — only metadata is read, because
# the arming job holds a write token. `workflow_run` runs the copy of this file
# on the DEFAULT branch, so this only takes effect once it lands on main.
on:
workflow_run: # zizmor: ignore[dangerous-triggers] runs from the base repo on ci completion; reads metadata only, never checks out or runs PR code
workflows: ["ci"]
types: [completed]
permissions: {}
# a later ci run for the same head supersedes an in-flight wait.
concurrency:
group: ci-auto-merge-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: true
jobs:
resolve:
# only PR runs of ci, and only green ones. a red ci never reaches the wait.
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
checks: read
outputs:
pr: ${{ steps.pr.outputs.pr }}
eligible: ${{ steps.checks.outputs.eligible }}
steps:
# workflow_run.pull_requests is empty for fork PRs — resolve via the
# commit->pulls endpoint instead (base token, no PR code executed).
- name: resolve the pull request
id: pr
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
pr=$(gh api "repos/$REPO/commits/$HEAD_SHA/pulls" --jq '.[0].number' 2>/dev/null || true)
if [ -z "$pr" ] || [ "$pr" = "null" ]; then
echo "no open PR for $HEAD_SHA"
echo "pr=" >> "$GITHUB_OUTPUT"
exit 0
fi
# the PR must still be open, undrafted, and still sitting on this sha.
# a merged/closed PR, or one that moved on, is not ours to touch.
read -r state draft head < <(gh pr view "$pr" --repo "$REPO" \
--json state,isDraft,headRefOid --jq '[.state,.isDraft,.headRefOid]|@tsv')
if [ "$state" != "OPEN" ] || [ "$draft" = "true" ] || [ "$head" != "$HEAD_SHA" ]; then
echo "PR #$pr not eligible (state=$state draft=$draft head=$head sha=$HEAD_SHA)"
echo "pr=" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "pr=$pr" >> "$GITHUB_OUTPUT"

# `ci` is only one of the workflows on a PR — the gates, the score job,
# the schema check and the label jobs are separate. "all the ci passed"
# means all of them, so wait them out rather than trusting branch
# protection (which `test` does not have).
# arm-auto-merge enforces this bar authoritatively and comments when a PR
# misses it. that comment is right for a human who just asked to arm, and
# wrong here — unattended, it would repeat on every push of every PR that
# has no owner ticket. so read the same link first and stay silent.
- name: require a closing issue opened by the owner
id: owner
if: steps.pr.outputs.pr != ''
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ steps.pr.outputs.pr }}
run: |
# SC2016: the query is single-quoted on purpose — $owner/$name/$pr are
# graphql variables bound by -f/-F, not shell expansions.
# shellcheck disable=SC2016
owners=$(gh api graphql \
-f owner="${REPO%/*}" -f name="${REPO#*/}" -F pr="$PR" \
-f query='query($owner:String!,$name:String!,$pr:Int!){
repository(owner:$owner,name:$name){
pullRequest(number:$pr){
closingIssuesReferences(first:50){nodes{number author{login}}}
}
}
}' \
--jq '[.data.repository.pullRequest.closingIssuesReferences.nodes[]
| select(.author.login=="plind-junior") | .number] | join(", ")' \
2>/dev/null || true)
if [ -n "$owners" ]; then
echo "closes owner-authored issue(s): $owners"
echo "ok=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "::notice::PR #$PR closes no issue opened by plind-junior; not arming auto-merge"
echo "ok=false" >> "$GITHUB_OUTPUT"

- name: wait for every check on the head sha, then require none failed
id: checks
if: steps.owner.outputs.ok == 'true'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
RUN_ID: ${{ github.run_id }}
run: |
echo "eligible=false" >> "$GITHUB_OUTPUT"
for _ in $(seq 1 40); do
# this workflow's own check run is excluded: it cannot wait on itself.
runs=$(gh api "repos/$REPO/commits/$HEAD_SHA/check-runs" --paginate \
--jq ".check_runs[] | select((.details_url // \"\") | contains(\"/runs/$RUN_ID/\") | not)
| [.status, (.conclusion // \"\")] | @tsv")
pending=$(printf '%s\n' "$runs" | grep -cv '^completed' || true)
if [ "$pending" -eq 0 ]; then
# success / skipped / neutral are all "did not fail". anything
# else — failure, cancelled, timed_out, action_required — blocks.
bad=$(printf '%s\n' "$runs" \
| awk -F'\t' '$2!="success" && $2!="skipped" && $2!="neutral"' | wc -l)
if [ "$bad" -eq 0 ]; then
echo "eligible=true" >> "$GITHUB_OUTPUT"
else
echo "::notice::checks failed on $HEAD_SHA; not arming auto-merge"
fi
exit 0
fi
sleep 30
done
echo "::notice::checks still running on $HEAD_SHA after 20m; not arming auto-merge"

# visible on the PR, and it is what makes deauthorize-on-push announce
# itself when a later push voids this. a label added with GITHUB_TOKEN
# does not re-trigger auto-merge.yml (github's token-recursion guard),
# so this does not double-arm.
- name: mark the PR as machine-authorized
if: steps.checks.outputs.eligible == 'true'
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ steps.pr.outputs.pr }}
run: gh pr edit "$PR" --repo "$REPO" --add-label auto-merge || true

arm:
needs: resolve
if: needs.resolve.outputs.eligible == 'true'
# a called workflow can only downgrade the caller's token, and this file
# starts from `permissions: {}` — so the grant has to be made here too.
permissions:
contents: write
pull-requests: write
checks: read
uses: ./.github/workflows/arm-auto-merge.yml
with:
pr: ${{ needs.resolve.outputs.pr }}
head_sha: ${{ github.event.workflow_run.head_sha }}
Loading
Loading