diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..06f6453 --- /dev/null +++ b/.github/CODEOWNERS @@ -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 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..44db631 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -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 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3a603f6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -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. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..ade569a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -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 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..111f822 --- /dev/null +++ b/.github/labeler.yml @@ -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/**" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..9dcc9f1 --- /dev/null +++ b/.github/pull_request_template.md @@ -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: diff --git a/.github/workflows/auto-response.yml b/.github/workflows/auto-response.yml new file mode 100644 index 0000000..a1ce283 --- /dev/null +++ b/.github/workflows/auto-response.yml @@ -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. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a90aa7..93136e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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." diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..cd65979 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -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, + }); diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..68687dd --- /dev/null +++ b/.github/workflows/stale.yml @@ -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. diff --git a/.github/workflows/workflow-sanity.yml b/.github/workflows/workflow-sanity.yml new file mode 100644 index 0000000..7c1391d --- /dev/null +++ b/.github/workflows/workflow-sanity.yml @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..56279a2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,158 @@ +# AGENTS.md — ZeroClaw Agent Coding Guide + +This file defines the default working protocol for coding agents in this repository. +Scope: entire repository. + +## 1) Project Snapshot (Read First) + +ZeroClaw is a Rust-first autonomous agent runtime optimized for: + +- high performance +- high efficiency +- high stability +- high extensibility +- high sustainability +- high security + +Core architecture is trait-driven and modular. Most extension work should be done by implementing traits and registering in factory modules. + +Key extension points: + +- `src/providers/traits.rs` (`Provider`) +- `src/channels/traits.rs` (`Channel`) +- `src/tools/traits.rs` (`Tool`) +- `src/memory/traits.rs` (`Memory`) +- `src/observability/traits.rs` (`Observer`) +- `src/runtime/traits.rs` (`RuntimeAdapter`) + +## 2) Repository Map (High-Level) + +- `src/main.rs` — CLI entrypoint and command routing +- `src/lib.rs` — module exports and shared command enums +- `src/config/` — schema + config loading/merging +- `src/agent/` — orchestration loop +- `src/gateway/` — webhook/gateway server +- `src/security/` — policy, pairing, secret store +- `src/memory/` — markdown/sqlite memory backends + embeddings/vector merge +- `src/providers/` — model providers and resilient wrapper +- `src/channels/` — Telegram/Discord/Slack/etc channels +- `src/tools/` — tool execution surface (shell, file, memory, browser) +- `src/runtime/` — runtime adapters (currently native) +- `docs/` — architecture + process docs +- `.github/` — CI, templates, automation workflows + +## 3) Non-Negotiable Engineering Constraints + +### 3.1 Performance and Footprint + +- Prefer minimal dependencies; avoid adding crates unless clearly justified. +- Preserve release-size profile assumptions in `Cargo.toml`. +- Avoid unnecessary allocations, clones, and blocking operations. +- Keep startup path lean; avoid heavy initialization in command parsing flow. + +### 3.2 Security and Safety + +- Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces. +- Never broaden filesystem/network execution scope without explicit policy checks. +- Never log secrets, tokens, raw credentials, or sensitive payloads. +- Keep default behavior secure-by-default (deny-by-default where applicable). + +### 3.3 Stability and Compatibility + +- Preserve CLI contract unless change is intentional and documented. +- Prefer explicit errors over silent fallback for unsupported critical paths. +- Keep changes local; avoid cross-module refactors in unrelated tasks. + +## 4) Agent Workflow (Required) + +1. **Read before write** + - Inspect existing module and adjacent tests before editing. +2. **Define scope boundary** + - One concern per PR; avoid mixed feature+refactor+infra patches. +3. **Implement minimal patch** + - Follow KISS/YAGNI/DRY; no speculative abstractions. +4. **Validate by risk** + - Docs-only: keep checks lightweight. + - Code changes: run relevant checks and tests. +5. **Document impact** + - Update docs/PR notes for behavior, risk, rollback. + +## 5) Change Playbooks + +### 5.1 Adding a Provider + +- Implement `Provider` in `src/providers/`. +- Register in `src/providers/mod.rs` factory. +- Add focused tests for factory wiring and error paths. + +### 5.2 Adding a Channel + +- Implement `Channel` in `src/channels/`. +- Ensure `send`, `listen`, and `health_check` semantics are consistent. +- Cover auth/allowlist/health behavior with tests. + +### 5.3 Adding a Tool + +- Implement `Tool` in `src/tools/` with strict parameter schema. +- Validate and sanitize all inputs. +- Return structured `ToolResult`; avoid panics in runtime path. + +### 5.4 Security / Runtime / Gateway Changes + +- Include threat/risk notes and rollback strategy. +- Add or update tests for boundary checks and failure modes. +- Keep observability useful but non-sensitive. + +## 6) Validation Matrix + +Default local checks for code changes: + +```bash +cargo fmt --all -- --check +cargo clippy --all-targets -- -D warnings +cargo test +``` + +If full checks are impractical, run the most relevant subset and document what was skipped and why. + +For workflow/template-only changes, at least ensure YAML/template syntax validity. + +## 7) Collaboration and PR Discipline + +- Follow `.github/pull_request_template.md`. +- Keep PR descriptions concrete: problem, change, non-goals, risk, rollback. +- Use conventional commit titles. +- Prefer small PRs (`size: XS/S/M`) when possible. + +Reference docs: + +- `CONTRIBUTING.md` +- `docs/pr-workflow.md` + +## 8) Anti-Patterns (Do Not) + +- Do not add heavy dependencies for minor convenience. +- Do not silently weaken security policy or access constraints. +- Do not mix massive formatting-only changes with functional changes. +- Do not modify unrelated modules "while here". +- Do not bypass failing checks without explicit explanation. + +## 9) Handoff Template (Agent -> Agent / Maintainer) + +When handing off work, include: + +1. What changed +2. What did not change +3. Validation run and results +4. Remaining risks / unknowns +5. Next recommended action + +## 10) Vibe Coding Guardrails + +When working in a fast iterative "vibe coding" style: + +- Keep each iteration reversible (small commits, clear rollback). +- Validate assumptions with code search before implementing. +- Prefer deterministic behavior over clever shortcuts. +- Do not "ship and hope" on security-sensitive paths. +- If uncertain, leave a concrete TODO with verification context, not a hidden guess. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c319cc5..c08857c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,6 +37,33 @@ git push --no-verify > **Note:** CI runs the same checks, so skipped hooks will be caught on the PR. +## High-Volume Collaboration Rules + +When PR traffic is high (especially with AI-assisted contributions), these rules keep quality and throughput stable: + +- **One concern per PR**: avoid mixing refactor + feature + infra in one change. +- **Small PRs first**: prefer PR size `XS/S/M`; split large work into stacked PRs. +- **Template is mandatory**: complete every section in `.github/pull_request_template.md`. +- **Explicit rollback**: every PR must include a fast rollback path. +- **Security-first review**: changes in `src/security/`, runtime, and CI need stricter validation. + +Full maintainer workflow: [`docs/pr-workflow.md`](docs/pr-workflow.md). + +## Agent Collaboration Guidance + +Agent-assisted contributions are welcome and treated as first-class contributions. + +For smoother agent-to-agent and human-to-agent review: + +- Keep PR summaries concrete (problem, change, non-goals). +- Include reproducible validation evidence (`fmt`, `clippy`, `test`, scenario checks). +- Add brief workflow notes when automation materially influenced design/code. +- Call out uncertainty and risky edges explicitly. + +We do **not** require PRs to declare an AI-vs-human line ratio. + +Agent implementation playbook lives in [`AGENTS.md`](AGENTS.md). + ## Architecture: Trait-Based Pluggability ZeroClaw's architecture is built on **traits** — every subsystem is swappable. This means contributing a new integration is as simple as implementing a trait and registering it in the factory function. @@ -184,8 +211,9 @@ impl Tool for YourTool { ## Pull Request Checklist -- [ ] `cargo fmt` — code is formatted -- [ ] `cargo clippy -- -D warnings` — no warnings +- [ ] PR template sections are completed (including security + rollback) +- [ ] `cargo fmt --all -- --check` — code is formatted +- [ ] `cargo clippy --all-targets -- -D warnings` — no warnings - [ ] `cargo test` — all 129+ tests pass - [ ] New code has inline `#[cfg(test)]` tests - [ ] No new dependencies unless absolutely necessary (we optimize for binary size) @@ -198,6 +226,7 @@ We use [Conventional Commits](https://www.conventionalcommits.org/): ``` feat: add Anthropic provider +feat(provider): add Anthropic provider fix: path traversal edge case with symlinks docs: update contributing guide test: add heartbeat unicode parsing tests @@ -205,6 +234,10 @@ refactor: extract common security checks chore: bump tokio to 1.43 ``` +Recommended scope keys in commit titles: + +- `provider`, `channel`, `memory`, `security`, `runtime`, `ci`, `docs`, `tests` + ## Code Style - **Minimal dependencies** — every crate adds to binary size @@ -219,6 +252,14 @@ chore: bump tokio to 1.43 - **Features**: Describe the use case, propose which trait to extend - **Security**: See [SECURITY.md](SECURITY.md) for responsible disclosure +## Maintainer Merge Policy + +- Require passing `CI Required Gate` before merge. +- Require review approval for non-trivial changes. +- Require CODEOWNERS review for protected paths. +- Prefer squash merge with conventional commit title. +- Revert fast on regressions; re-land with tests. + ## License By contributing, you agree that your contributions will be licensed under the MIT License. diff --git a/docs/pr-workflow.md b/docs/pr-workflow.md new file mode 100644 index 0000000..d34826c --- /dev/null +++ b/docs/pr-workflow.md @@ -0,0 +1,178 @@ +# ZeroClaw PR Workflow (High-Volume Collaboration) + +This document defines how ZeroClaw handles high PR volume while maintaining: + +- High performance +- High efficiency +- High stability +- High extensibility +- High sustainability +- High security + +## 1) Governance Goals + +1. Keep merge throughput predictable under heavy PR load. +2. Keep CI signal quality high (fast feedback, low false positives). +3. Keep security review explicit for risky surfaces. +4. Keep changes easy to reason about and easy to revert. + +## 2) Required Repository Settings + +Maintain these branch protection rules on `main`: + +- Require status checks before merge. +- Require check `CI Required Gate`. +- Require pull request reviews before merge. +- Require CODEOWNERS review for protected paths. +- Dismiss stale approvals when new commits are pushed. +- Restrict force-push on protected branches. + +## 3) PR Lifecycle + +### Step A: Intake + +- Contributor opens PR with full `.github/pull_request_template.md`. +- `PR Labeler` applies path labels + size labels. +- `Auto Response` posts first-time contributor guidance. + +### Step B: Validation + +- `CI Required Gate` is the merge gate. +- Docs-only PRs use fast-path and skip heavy Rust jobs. +- Non-doc PRs must pass lint, tests, and release build smoke check. + +### Step C: Review + +- Reviewers prioritize by risk and size labels. +- Security-sensitive paths (`src/security`, runtime, CI) require maintainer attention. +- Large PRs (`size: L`/`size: XL`) should be split unless strongly justified. + +### Step D: Merge + +- Prefer **squash merge** to keep history compact. +- PR title should follow Conventional Commit style. +- Merge only when rollback path is documented. + +## 4) PR Size Policy + +- `size: XS` <= 80 changed lines +- `size: S` <= 250 changed lines +- `size: M` <= 500 changed lines +- `size: L` <= 1000 changed lines +- `size: XL` > 1000 changed lines + +Policy: + +- Target `XS/S/M` by default. +- `L/XL` PRs need explicit justification and tighter test evidence. +- If a large feature is unavoidable, split into stacked PRs. + +## 5) AI/Agent Contribution Policy + +AI-assisted PRs are welcome, and review can also be agent-assisted. + +Required: + +1. Clear PR summary with scope boundary. +2. Explicit test/validation evidence. +3. Security impact and rollback notes for risky changes. + +Recommended: + +1. Brief tool/workflow notes when automation materially influenced the change. +2. Optional prompt/plan snippets for reproducibility. + +We do **not** require contributors to quantify AI-vs-human line ownership. + +Review emphasis for AI-heavy PRs: + +- Contract compatibility +- Security boundaries +- Error handling and fallback behavior +- Performance and memory regressions + +## 6) Review SLA and Queue Discipline + +- First maintainer triage target: within 48 hours. +- If PR is blocked, maintainer leaves one actionable checklist. +- `stale` automation is used to keep queue healthy; maintainers can apply `no-stale` when needed. + +## 7) Security and Stability Rules + +Changes in these areas require stricter review and stronger test evidence: + +- `src/security/**` +- runtime process management +- filesystem access boundaries +- network/authentication behavior +- GitHub workflows and release pipeline + +Minimum for risky PRs: + +- threat/risk statement +- mitigation notes +- rollback steps + +## 8) Failure Recovery + +If a merged PR causes regressions: + +1. Revert PR immediately on `main`. +2. Open a follow-up issue with root-cause analysis. +3. Re-introduce fix only with regression tests. + +Prefer fast restore of service quality over delayed perfect fixes. + +## 9) Maintainer Checklist (Merge-Ready) + +- Scope is focused and understandable. +- CI gate is green. +- Security impact fields are complete. +- Agent workflow notes are sufficient for reproducibility (if automation was used). +- Rollback plan is explicit. +- Commit title follows Conventional Commits. + +## 10) Agent Review Operating Model + +To keep review quality stable under high PR volume, we use a two-lane review model: + +### Lane A: Fast triage (agent-friendly) + +- Confirm PR template completeness. +- Confirm CI gate signal (`CI Required Gate`). +- Confirm risk class via labels and touched paths. +- Confirm rollback statement exists. + +### Lane B: Deep review (risk-based) + +Required for high-risk changes (security/runtime/gateway/CI): + +- Validate threat model assumptions. +- Validate failure mode and degradation behavior. +- Validate backward compatibility and migration impact. +- Validate observability/logging impact. + +## 11) Queue Priority and Label Discipline + +Triage order recommendation: + +1. `size: XS`/`size: S` + bug/security fixes +2. `size: M` focused changes +3. `size: L`/`size: XL` split requests or staged review + +Label discipline: + +- Path labels identify subsystem ownership quickly. +- Size labels drive batching strategy. +- `no-stale` is reserved for accepted-but-blocked work. + +## 12) Agent Handoff Contract + +When one agent hands off to another (or to a maintainer), include: + +1. Scope boundary (what changed / what did not). +2. Validation evidence. +3. Open risks and unknowns. +4. Suggested next action. + +This keeps context loss low and avoids repeated deep dives.