docs(agents): add downstream repository checklist to the PR template - #3298
Conversation
Cozystack is upstream for repositories that nothing keeps in sync with it, and no CI job compares the two sides, so a change here breaks them in silence. Only two narrow paths are automated, and both fire on a release rather than on a PR. It has already happened. The website docs generator works from an app list hardcoded in its own Makefile, and packages/apps/opensearch was never added to it, so an application we ship has no reference page at all. The Terraform provider still offers a Kubernetes version this repo removed. The plugin repo still sends operators to hack/ scripts that were deleted, one of them rewritten as a Go command. Add a Downstream Repositories trigger map to the contributing guide, naming per repository which change here forces a change over there and which file to touch, and a checklist in the PR template that links to it. The template addresses AI agents directly: they author a large share of PRs and will otherwise tick a box without opening the follow-up it claims. The coupling is wider than the obvious ones, and mostly invisible. talm restates the node contract, overlapping ours but already diverged, so it must be diffed rather than overwritten. cozyhr reads an annotation the operator writes and silently merges the wrong values if it is renamed. cozy-proxy is vendored in from its own repo, yet consumes labels produced here and quietly stops handling external VM services if they move. The telemetry server queries metric names emitted here, and a rename just empties its dashboards. None of these fail loudly. Guard the map with a unit test, because a map about silent drift must not drift silently itself. It pins every in-repo path the map cites to a file that exists, pins the map and the checklist to the same repository list, and pins the workflow carve-out below to the map's location, to the job that reads the flag, and to the output that carries it: cutting any one of those three leaves the other two looking wired up. The header states plainly what the suite cannot check, so a green tick is not mistaken for a correct map: whether a coupling is real, and whether it still holds over there, stays a human's job. Editing the map alone is a docs-only PR, which skips the unit tests and would exempt the map from its own guard. Rather than widen the docs-only rule, which also gates the Talos image build, the plan step exports a separate flag that only the unit-test job reads: a docs PR now runs the tests and still builds nothing. It detects the map by its old path too, since a rename is reported as the new path alone. Document that gh pr create --body and --body-file replace the body wholesale, which would otherwise drop the checklist from every CLI-authored PR. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses the silent breakage of downstream repositories by introducing a mandatory checklist and a comprehensive trigger map. By explicitly documenting coupling points and enforcing a follow-up process, the changes ensure that contributors and AI agents are aware of the impact their changes have on satellite repositories, reducing the risk of drift and broken integrations. Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe PR adds downstream repository guidance and checklist content, triggers unit tests when the trigger map changes, and adds Bats coverage for documented paths, workflow wiring, and repository-list consistency. ChangesDownstream trigger map
Sequence Diagram(s)sequenceDiagram
participant PRDiff
participant PlanJob
participant ChecksJob
participant TriggerMapTests
PRDiff->>PlanJob: detect contributing guide changes
PlanJob->>ChecksJob: set trigger_map output
ChecksJob->>TriggerMapTests: run make unit-tests
TriggerMapTests->>TriggerMapTests: validate map, workflow, and repository lists
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a "Downstream Repositories" trigger map and checklist to track changes affecting downstream repositories, accompanied by a BATS test suite to ensure the map's paths and repository lists remain valid and synchronized. The reviewer suggested making the workflow parsing in the BATS test more robust by normalizing whitespace and quotes to prevent fragility against minor formatting changes in the workflow YAML.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| code_only="$(sed 's/#.*//' "$WORKFLOW")" | ||
|
|
||
| # The block of the job that runs the unit tests, from its header to the next | ||
| # job's. The condition has to sit on THAT job: pinning it file-wide would accept | ||
| # the exact expression pasted into some other job while the unit-test job quietly | ||
| # loses it. | ||
| unit_job="$(printf '%s\n' "$code_only" | awk ' | ||
| /^ [a-zA-Z0-9_-]+:[[:space:]]*$/ { inside = ($0 == " checks:") } | ||
| inside')" | ||
| printf '%s\n' "$unit_job" | grep -q 'make unit-tests' || { | ||
| echo "The 'checks' job in .github/workflows/pull-requests.yaml no longer runs 'make unit-tests'." >&2 | ||
| echo "This suite pins the trigger-map carve-out to that job. If the unit tests moved, point" >&2 | ||
| echo "the checks below at their new job." >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| # Three links carry the carve-out: the plan step names the map, the plan job | ||
| # exports the flag, and the unit-test job gates on it. Cutting any one leaves the | ||
| # other two looking perfectly wired, and none of them fails loudly — an unset or | ||
| # unexported output dereferences to an empty string, so the condition is merely | ||
| # false and the suite is skipped in silence, on exactly the PRs it guards, with | ||
| # every test in this file still green. Pin all three. | ||
| rel="${MAP_FILE#"$REPO_ROOT"/}" | ||
| printf '%s\n' "$code_only" | grep -qF "'$rel'" || { | ||
| echo "The plan step in .github/workflows/pull-requests.yaml does not name '$rel'." >&2 | ||
| echo "Without it, a PR that only edits the trigger map is treated as docs-only, the unit" >&2 | ||
| echo "tests are skipped, and this file never runs — exactly when it is needed most." >&2 | ||
| echo "Fix: point the trigger_map detection at the map's new path." >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| printf '%s\n' "$code_only" | grep -qF 'trigger_map: ${{ steps.p.outputs.trigger_map }}' || { | ||
| echo "The plan job in .github/workflows/pull-requests.yaml does not export trigger_map." >&2 | ||
| echo "The step still computes it and the unit-test job still reads it, so this looks wired" >&2 | ||
| echo "up, but an unexported output dereferences to an empty string: the job is skipped in" >&2 | ||
| echo "silence on exactly the PRs this suite guards." >&2 | ||
| echo "Fix: restore 'trigger_map: \${{ steps.p.outputs.trigger_map }}' to the plan job's outputs." >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| condition="(needs.plan.outputs.code == 'true' || needs.plan.outputs.trigger_map == 'true')" | ||
| printf '%s\n' "$unit_job" | grep -qF "$condition" || { | ||
| echo "The job that runs 'make unit-tests' is not gated on:" >&2 | ||
| echo " $condition" >&2 | ||
| echo "Mentioning trigger_map in another job, or in a comment, does not count. Without that" >&2 | ||
| echo "exact condition on that job, a PR which only edits the trigger map stays docs-only," >&2 | ||
| echo "skips the unit tests, and never runs this file — while every test here still passes." >&2 | ||
| exit 1 | ||
| } |
There was a problem hiding this comment.
The current implementation of the workflow checks uses exact string matching (grep -qF) on multi-line YAML structures. This is highly fragile to minor formatting changes in .github/workflows/pull-requests.yaml (such as changing single quotes to double quotes, adding/removing trailing whitespace, or altering spacing around operators).
We can make these checks extremely robust by normalizing the whitespace and quotes (e.g., using tr -d "[:space:]'\"") before performing the substring matches. Additionally, we can refine the awk pattern for finding the checks job to safely handle trailing spaces or carriage returns.
code_only="$(sed 's/#.*//' "$WORKFLOW")"
normalized_code="$(printf '%s\n' "$code_only" | tr -d "[:space:]'\"")"
# The block of the job that runs the unit tests, from its header to the next
# job's. The condition has to sit on THAT job: pinning it file-wide would accept
# the exact expression pasted into some other job while the unit-test job quietly
# loses it.
unit_job="$(printf '%s\n' "$code_only" | awk '
/^ [a-zA-Z0-9_-]+:[[:space:]]*$/ { inside = ($0 ~ /^ checks:[[:space:]]*$/) }
inside')"
normalized_unit_job="$(printf '%s\n' "$unit_job" | tr -d "[:space:]'\"")"
printf '%s\n' "$unit_job" | grep -q 'make unit-tests' || {
echo "The 'checks' job in .github/workflows/pull-requests.yaml no longer runs 'make unit-tests'." >&2
echo "This suite pins the trigger-map carve-out to that job. If the unit tests moved, point"
echo "the checks below at their new job." >&2
exit 1
}
# Three links carry the carve-out: the plan step names the map, the plan job
# exports the flag, and the unit-test job gates on it. Cutting any one leaves the
# other two looking perfectly wired, and none of them fails loudly — an unset or
# unexported output dereferences to an empty string, so the condition is merely
# false and the suite is skipped in silence, on exactly the PRs it guards, with
# every test in this file still green. Pin all three.
rel="${MAP_FILE#"$REPO_ROOT"/}"
printf '%s\n' "$normalized_code" | grep -qF "$rel" || {
echo "The plan step in .github/workflows/pull-requests.yaml does not name '$rel'." >&2
echo "Without it, a PR that only edits the trigger map is treated as docs-only, the unit"
echo "tests are skipped, and this file never runs — exactly when it is needed most." >&2
echo "Fix: point the trigger_map detection at the map's new path." >&2
exit 1
}
printf '%s\n' "$normalized_code" | grep -qF 'trigger_map:${{steps.p.outputs.trigger_map}}' || {
echo "The plan job in .github/workflows/pull-requests.yaml does not export trigger_map." >&2
echo "The step still computes it and the unit-test job still reads it, so this looks wired"
echo "up, but an unexported output dereferences to an empty string: the job is skipped in"
echo "silence on exactly the PRs this suite guards." >&2
echo "Fix: restore 'trigger_map: \${{ steps.p.outputs.trigger_map }}' to the plan job's outputs." >&2
exit 1
}
condition_norm="(needs.plan.outputs.code==true||needs.plan.outputs.trigger_map==true)"
printf '%s\n' "$normalized_unit_job" | grep -qF "$condition_norm" || {
echo "The job that runs 'make unit-tests' is not gated on:" >&2
echo " (needs.plan.outputs.code == 'true' || needs.plan.outputs.trigger_map == 'true')" >&2
echo "Mentioning trigger_map in another job, or in a comment, does not count. Without that"
echo "exact condition on that job, a PR which only edits the trigger map stays docs-only,"
echo "skips the unit tests, and never runs this file — while every test here still passes." >&2
exit 1
}
myasnikovdaniil
left a comment
There was a problem hiding this comment.
LGTM.
The trigger-map + PR-template checklist + hack/downstream-trigger-map.bats guard are internally consistent — the workflow carve-out is wired across all three links (plan-step detection, output export, and the checks job if:), the map/template repository lists agree (10↔10), the rename tripwire (git diff --no-renames + grep -qxF) and the anti-vacuum floor back each other up, and every cited shared-root path resolves (28 paths, 0 missing). Guard scope is honest: it validates the map's internal consistency when contributing.md changes; the checklist itself stays advisory — nothing in CI blocks a PR that skips it. One non-blocking note inline.
| needs: ["plan"] | ||
| # trigger_map opts a docs-only PR back in when it edits the downstream trigger | ||
| # map, so hack/downstream-trigger-map.bats still guards it. No other job reads | ||
| # that flag: a docs PR runs the unit tests and nothing heavier. |
There was a problem hiding this comment.
Minor wording: the checks job runs both make unit-tests and make test-controllers, so a docs-only edit that trips this flag also runs the envtest controller suite — “nothing heavier” is slightly optimistic. Still safe (that job builds no images), so the intent holds.
What this PR does
Cozystack is upstream for repositories that nothing keeps in sync with it, and no CI job compares the two sides, so a change here can break them in silence. It has already happened:
packages/apps/opensearchships but has no reference page on the website, the Terraform provider still offers a Kubernetes version this repo removed, and the plugin repo still sends operators to ahack/script that was deleted.This adds a Downstream Repositories trigger map to
docs/agents/contributing.md— per repository, which change here forces a change over there and which file to touch — and a checklist in the PR template that links to it. The template addresses AI agents directly, since they author a large share of PRs, and tells them to open the follow-up rather than tick a box that claims work nobody did.A unit test (
hack/downstream-trigger-map.bats) keeps the map from rotting: it pins every in-repo path the map cites to a file that exists, pins the map and the checklist to the same repository list, and pins the CI carve-out that keeps the test running when the map is edited alone. Its header is explicit about what it cannot check — whether a coupling is real, and whether it still holds in the other repo, stays a human's job.Downstream repositories
Release note
Summary by CodeRabbit
Documentation
CI
Tests