chore(ci): establish PR governance for agent collaboration (#177)

* chore(ci): establish PR governance for agent collaboration

* docs: add AGENTS playbook and strengthen agent collaboration workflow

---------

Co-authored-by: chumyin <183474434+chumyin@users.noreply.github.com>
This commit is contained in:
Chummy 2026-02-16 01:41:16 +08:00 committed by GitHub
parent dca95cac7a
commit dfe648d5ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 1020 additions and 19 deletions

10
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1,10 @@
# Default owner for all files
* @theonlyhennygod
# High-risk surfaces
/src/security/** @theonlyhennygod
/src/runtime/** @theonlyhennygod
/src/memory/** @theonlyhennygod
/.github/** @theonlyhennygod
/Cargo.toml @theonlyhennygod
/Cargo.lock @theonlyhennygod

93
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View file

@ -0,0 +1,93 @@
name: Bug Report
description: Report a reproducible defect in ZeroClaw
title: "[Bug]: "
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to report a bug.
Please provide a minimal reproducible case so maintainers can triage quickly.
- type: input
id: summary
attributes:
label: Summary
description: One-line description of the problem.
placeholder: zeroclaw daemon exits immediately when ...
validations:
required: true
- type: textarea
id: current
attributes:
label: Current behavior
description: What is happening now?
placeholder: The process exits with ...
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: What should happen instead?
placeholder: The daemon should stay alive and ...
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: Please provide exact commands/config.
placeholder: |
1. zeroclaw onboard --interactive
2. zeroclaw daemon
3. Observe crash in logs
render: bash
validations:
required: true
- type: textarea
id: logs
attributes:
label: Logs / stack traces
description: Paste relevant logs (redact secrets).
render: text
validations:
required: false
- type: input
id: version
attributes:
label: ZeroClaw version
placeholder: v0.1.0 / commit SHA
validations:
required: true
- type: input
id: rust
attributes:
label: Rust version
placeholder: rustc 1.xx.x
validations:
required: true
- type: input
id: os
attributes:
label: Operating system
placeholder: Ubuntu 24.04 / macOS 15 / Windows 11
validations:
required: true
- type: dropdown
id: regression
attributes:
label: Regression?
options:
- Unknown
- Yes, it worked before
- No, first-time setup
validations:
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability report
url: https://github.com/theonlyhennygod/zeroclaw/security/policy
about: Please report security vulnerabilities privately via SECURITY.md policy.
- name: Contribution guide
url: https://github.com/theonlyhennygod/zeroclaw/blob/main/CONTRIBUTING.md
about: Please read contribution and PR requirements before opening an issue.

View file

@ -0,0 +1,64 @@
name: Feature Request
description: Propose an improvement or new capability
title: "[Feature]: "
body:
- type: markdown
attributes:
value: |
Thanks for sharing your idea.
Please focus on user value, constraints, and rollout safety.
- type: input
id: problem
attributes:
label: Problem statement
description: What user problem are you trying to solve?
placeholder: Teams need a way to ...
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: Describe the preferred solution.
placeholder: Add a new subcommand / trait implementation ...
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: What alternatives did you evaluate?
placeholder: Keep current behavior, use external tool, etc.
validations:
required: false
- type: textarea
id: architecture
attributes:
label: Architecture impact
description: Which subsystem(s) are affected?
placeholder: providers/, channels/, memory/, runtime/, security/ ...
validations:
required: true
- type: textarea
id: risk
attributes:
label: Risk and rollback
description: Main risk + how to disable/revert quickly.
placeholder: Risk is ... rollback is ...
validations:
required: true
- type: dropdown
id: breaking
attributes:
label: Breaking change?
options:
- No
- Yes
validations:
required: true

59
.github/labeler.yml vendored Normal file
View file

@ -0,0 +1,59 @@
"type: docs":
- changed-files:
- any-glob-to-any-file:
- "docs/**"
- "**/*.md"
- "LICENSE"
"type: dependencies":
- changed-files:
- any-glob-to-any-file:
- "Cargo.toml"
- "Cargo.lock"
- "deny.toml"
"type: ci":
- changed-files:
- any-glob-to-any-file:
- ".github/**"
- ".githooks/**"
"area: providers":
- changed-files:
- any-glob-to-any-file:
- "src/providers/**"
"area: channels":
- changed-files:
- any-glob-to-any-file:
- "src/channels/**"
"area: memory":
- changed-files:
- any-glob-to-any-file:
- "src/memory/**"
"area: security":
- changed-files:
- any-glob-to-any-file:
- "src/security/**"
"area: runtime":
- changed-files:
- any-glob-to-any-file:
- "src/runtime/**"
"area: tools":
- changed-files:
- any-glob-to-any-file:
- "src/tools/**"
"area: observability":
- changed-files:
- any-glob-to-any-file:
- "src/observability/**"
"area: tests":
- changed-files:
- any-glob-to-any-file:
- "tests/**"

70
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,70 @@
## Summary
Describe this PR in 2-5 bullets:
- Problem:
- Why it matters:
- What changed:
- What did **not** change (scope boundary):
## Change Type
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore / infra
## Scope
- [ ] Core runtime / daemon
- [ ] Provider integration
- [ ] Channel integration
- [ ] Memory / storage
- [ ] Security / sandbox
- [ ] CI / release / tooling
- [ ] Documentation
## Linked Issue
- Closes #
- Related #
## Testing
Commands and result summary (required):
```bash
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test
```
If any command is intentionally skipped, explain why.
## Security Impact
- New permissions/capabilities? (`Yes/No`)
- New external network calls? (`Yes/No`)
- Secrets/tokens handling changed? (`Yes/No`)
- File system access scope changed? (`Yes/No`)
- If any `Yes`, describe risk and mitigation:
## Agent Collaboration Notes (recommended)
- [ ] If agent/automation tools were used, I added brief workflow notes.
- [ ] I included concrete validation evidence for this change.
- [ ] I can explain design choices and rollback steps.
If agent tools were used, optional context:
- Tool(s):
- Prompt/plan summary:
- Verification focus:
## Rollback Plan
- Fast rollback command/path:
- Feature flags or config toggles (if any):
- Observable failure symptoms:

40
.github/workflows/auto-response.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: Auto Response
on:
issues:
types: [opened]
pull_request_target:
types: [opened]
permissions: {}
jobs:
first-interaction:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Greet first-time contributors
uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: |
Thanks for opening this issue.
Before maintainers triage it, please confirm:
- Repro steps are complete and run on latest `main`
- Environment details are included (OS, Rust version, ZeroClaw version)
- Sensitive values are redacted
This helps us keep issue throughput high and response latency low.
pr-message: |
Thanks for contributing to ZeroClaw.
For faster review, please ensure:
- PR template sections are fully completed
- `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` are included
- If automation/agents were used heavily, add brief workflow notes
- Scope is focused (prefer one concern per PR)
See `CONTRIBUTING.md` and `docs/pr-workflow.md` for full collaboration rules.

View file

@ -6,14 +6,86 @@ on:
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
jobs:
changes:
name: Detect Change Scope
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.scope.outputs.docs_only }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect docs-only changes
id: scope
shell: bash
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
else
BASE="${{ github.event.before }}"
fi
if [ -z "$BASE" ] || ! git cat-file -e "$BASE^{commit}" 2>/dev/null; then
echo "docs_only=false" >> "$GITHUB_OUTPUT"
exit 0
fi
CHANGED="$(git diff --name-only "$BASE" HEAD || true)"
if [ -z "$CHANGED" ]; then
echo "docs_only=false" >> "$GITHUB_OUTPUT"
exit 0
fi
docs_only=true
while IFS= read -r file; do
[ -z "$file" ] && continue
case "$file" in
docs/*|*.md|*.mdx|LICENSE|.github/ISSUE_TEMPLATE/*|.github/pull_request_template.md)
;;
*)
docs_only=false
break
;;
esac
done <<< "$CHANGED"
echo "docs_only=$docs_only" >> "$GITHUB_OUTPUT"
lint:
name: Format & Lint
needs: [changes]
if: needs.changes.outputs.docs_only != 'true'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Run rustfmt
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets -- -D warnings
test:
name: Test
needs: [changes]
if: needs.changes.outputs.docs_only != 'true'
runs-on: ubuntu-latest
continue-on-error: true # Don't block PRs
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
@ -22,24 +94,55 @@ jobs:
run: cargo test --verbose
build:
name: Build
runs-on: ${{ matrix.os }}
continue-on-error: true # Don't block PRs on build failures
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: x86_64-apple-darwin
- os: macos-latest
target: aarch64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
name: Build (Smoke)
needs: [changes]
if: needs.changes.outputs.docs_only != 'true'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build
run: cargo build --release --verbose
- name: Build release binary
run: cargo build --release --locked --verbose
docs-only:
name: Docs-Only Fast Path
needs: [changes]
if: needs.changes.outputs.docs_only == 'true'
runs-on: ubuntu-latest
steps:
- name: Skip heavy jobs for docs-only change
run: echo "Docs-only change detected. Rust lint/test/build skipped."
ci-required:
name: CI Required Gate
if: always()
needs: [changes, lint, test, build, docs-only]
runs-on: ubuntu-latest
steps:
- name: Enforce required status
shell: bash
run: |
set -euo pipefail
if [ "${{ needs.changes.outputs.docs_only }}" = "true" ]; then
echo "Docs-only fast path passed."
exit 0
fi
lint_result="${{ needs.lint.result }}"
test_result="${{ needs.test.result }}"
build_result="${{ needs.build.result }}"
echo "lint=${lint_result}"
echo "test=${test_result}"
echo "build=${build_result}"
if [ "$lint_result" != "success" ] || [ "$test_result" != "success" ] || [ "$build_result" != "success" ]; then
echo "Required CI jobs did not pass."
exit 1
fi
echo "All required CI jobs passed."

70
.github/workflows/labeler.yml vendored Normal file
View file

@ -0,0 +1,70 @@
name: PR Labeler
on:
pull_request_target:
types: [opened, reopened, synchronize, edited]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Apply path labels
uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Apply size label
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const sizeLabels = ["size: XS", "size: S", "size: M", "size: L", "size: XL"];
const labelColor = "BFDADC";
const changedLines = (pr.additions || 0) + (pr.deletions || 0);
let sizeLabel = "size: XL";
if (changedLines <= 80) sizeLabel = "size: XS";
else if (changedLines <= 250) sizeLabel = "size: S";
else if (changedLines <= 500) sizeLabel = "size: M";
else if (changedLines <= 1000) sizeLabel = "size: L";
for (const label of sizeLabels) {
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label,
});
} catch (error) {
if (error.status !== 404) throw error;
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label,
color: labelColor,
});
}
}
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
});
const keepLabels = currentLabels
.map((label) => label.name)
.filter((label) => !sizeLabels.includes(label));
const nextLabels = [...new Set([...keepLabels, sizeLabel])];
await github.rest.issues.setLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: nextLabels,
});

44
.github/workflows/stale.yml vendored Normal file
View file

@ -0,0 +1,44 @@
name: Stale
on:
schedule:
- cron: "20 2 * * *"
workflow_dispatch:
permissions: {}
jobs:
stale:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Mark stale issues and pull requests
uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 21
days-before-issue-close: 7
days-before-pr-stale: 14
days-before-pr-close: 7
stale-issue-label: stale
stale-pr-label: stale
exempt-issue-labels: security,pinned,no-stale,maintainer
exempt-pr-labels: no-stale,maintainer
remove-stale-when-updated: true
exempt-all-assignees: true
operations-per-run: 300
stale-issue-message: |
This issue was automatically marked as stale due to inactivity.
Please provide an update, reproduction details, or current status to keep it open.
close-issue-message: |
Closing this issue due to inactivity.
If the problem still exists on the latest `main`, please open a new issue with fresh repro steps.
close-issue-reason: not_planned
stale-pr-message: |
This PR was automatically marked as stale due to inactivity.
Please rebase/update and post the latest validation results.
close-pr-message: |
Closing this PR due to inactivity.
Maintainers can reopen once the branch is updated and validation is provided.

63
.github/workflows/workflow-sanity.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Workflow Sanity
on:
pull_request:
paths:
- ".github/workflows/**"
- ".github/*.yml"
- ".github/*.yaml"
push:
branches: [main]
paths:
- ".github/workflows/**"
- ".github/*.yml"
- ".github/*.yaml"
concurrency:
group: workflow-sanity-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
jobs:
no-tabs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Fail on tabs in workflow files
shell: bash
run: |
set -euo pipefail
python - <<'PY'
from __future__ import annotations
import pathlib
import sys
root = pathlib.Path(".github/workflows")
bad: list[str] = []
for path in sorted(root.rglob("*.yml")):
if b"\t" in path.read_bytes():
bad.append(str(path))
for path in sorted(root.rglob("*.yaml")):
if b"\t" in path.read_bytes():
bad.append(str(path))
if bad:
print("Tabs found in workflow file(s):")
for path in bad:
print(f"- {path}")
sys.exit(1)
PY
actionlint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Lint GitHub workflows
uses: rhysd/actionlint@v1