Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ updates:
directory: /
schedule:
interval: weekly
cooldown:
default-days: 7
- package-ecosystem: rust-toolchain
directory: /
schedule:
interval: weekly
cooldown:
default-days: 7
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
cooldown:
default-days: 7
32 changes: 12 additions & 20 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
python-version: '3.13'
- name: Check packaging policy
run: |
python3 -m pip install PyYAML==6.0.3
python3 -m pip install PyYAML==6.0.3 pre-commit==4.5.1
python3 packaging/test_policy.py

# Dependabot raises version bumps. This reports whether the tree as locked carries a
Expand All @@ -41,27 +41,19 @@ jobs:
with:
command: check

# Project invariants clippy cannot express. Not a security scan: CodeQL already
# analyses this crate, and CodeRabbit keeps running its own opengrep packs because the
# ruleset is deliberately not named so CodeRabbit adopts it.
rules:
name: Custom rules
# Workflow-level audit: permissions, injection, unpinned actions. The other gates
# look at the crate and the packages, not at the workflows themselves.
zizmor:
name: Workflow audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install opengrep
env:
OPENGREP_VERSION: v1.30.0
OPENGREP_SHA256: 35779bdd72e92129c8df2a77f0c55e8c08356801ea92591ef32108d6b28d564c
run: |
curl -fsSL -o /usr/local/bin/opengrep \
"https://github.com/opengrep/opengrep/releases/download/${OPENGREP_VERSION}/opengrep_manylinux_x86"
echo "${OPENGREP_SHA256} /usr/local/bin/opengrep" | sha256sum -c -
chmod +x /usr/local/bin/opengrep
opengrep --version
- name: Run the rule-tests
run: ./scripts/opengrep-test.sh
- name: Run the custom ruleset
run: ./scripts/opengrep-scan.sh
# advanced-security uploads SARIF and needs security-events: write, which a called
# workflow cannot hold unless every caller grants it. The findings gate the job and
# appear as annotations, so the upload buys nothing worth widening the callers for.
- uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
with:
advanced-security: false
annotations: true
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:

jobs:
check:
uses: ./.github/workflows/checks.yml
uses: $/.github/workflows/checks.yml

packages:
uses: ./.github/workflows/packages.yml
uses: $/.github/workflows/packages.yml
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
# The release must not publish while the test gate is red.
check:
if: github.event_name == 'push'
uses: ./.github/workflows/checks.yml
uses: $/.github/workflows/checks.yml

# build_command runs only during a release, so a broken one would surface on main
# with the release already half done. Run it here against the version a release
Expand Down
24 changes: 17 additions & 7 deletions .opengrep/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,30 @@ stop coming back review after review.

## Relationship to CodeRabbit

CodeRabbit auto-detects an opengrep config only when it is named `opengrep.yml` or
`semgrep.yml` (and a few variants), and when it finds one it runs *that* **instead of** its
default packs. This ruleset deliberately avoids those names, so CodeRabbit keeps running
its own packs while these rules are enforced separately by `scripts/opengrep-scan.sh` and
the CI job. Both rulesets apply.
CodeRabbit steps aside from opengrep in two separate ways, and this setup avoids both.

It auto-detects an opengrep config only when it is named `opengrep.yml` or `semgrep.yml`
(and a few variants), and when it finds one it runs *that* **instead of** its default
packs. This ruleset deliberately avoids those names.

It also skips its own opengrep pass when it sees opengrep running in the workflows. These
rules therefore run from a **local pre-commit hook only**, never in CI. Running them in CI
would trade CodeRabbit's broad packs for this repo's two narrow rules, which is a straight
loss. A policy test asserts no workflow mentions opengrep.

Install the hook with `pre-commit install --install-hooks`. Both hooks carry a `files`
filter, because the scripts scan the whole crate and would otherwise run on every commit:
the scan runs when `src/` or the rules change, the rule-tests only when the rules change. A
commit touching neither costs nothing.

## Layout

| Path | Purpose |
| --- | --- |
| `.opengrep/agentx-ifstack-rules.yaml` | The ruleset, and the single source of truth. Named so CodeRabbit does not adopt it. |
| `.opengrep/tests/*.rs` | Rule-test fixtures. `// ruleid:` must match, `// ok:` must not. They violate the rules on purpose and are not part of the crate. |
| `scripts/opengrep-scan.sh` | Scan `src/`. Exits non-zero on any finding. |
| `scripts/opengrep-test.sh` | Run the rule-tests against the ruleset. |
| `scripts/opengrep-scan.sh` | Scan `src/`. Exits non-zero on any finding. Wired to pre-commit by `.pre-commit-config.yaml`. |
| `scripts/opengrep-test.sh` | Run the rule-tests against the ruleset. Runs when the rules change. |

## Rules

Expand Down
27 changes: 27 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Local hooks only. The repository's other gates run in CI; this file exists so the
# custom opengrep ruleset runs somewhere, because CodeRabbit skips its own opengrep pass
# when it sees opengrep in the workflows. See .opengrep/README.md.
#
# Install with: pre-commit install --install-hooks
repos:
- repo: local
hooks:
# The scripts scan the whole crate, so filenames are not passed. `files` still
# decides whether the hook runs at all, which keeps a docs-only commit free.
- id: opengrep-rule-tests
name: opengrep rule-tests
entry: scripts/opengrep-test.sh
language: script
pass_filenames: false
files: ^\.opengrep/
# Rules changing can flag code that already exists, so run the scan for those too.
- id: opengrep
name: opengrep (custom agentx-ifstack rules)
entry: scripts/opengrep-scan.sh
language: script
pass_filenames: false
files: ^(src/.*\.rs|\.opengrep/.*)$

default_install_hook_types: [pre-commit]
default_stages: [pre-commit]
fail_fast: false
30 changes: 29 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,17 @@ as a separate job in `checks.yml`.
Project invariants that clippy cannot express live in `.opengrep/agentx-ifstack-rules.yaml`.
Run `scripts/opengrep-scan.sh` to check the source and `scripts/opengrep-test.sh` to check
the rules themselves. Every rule needs a fixture in `.opengrep/tests/`, which the packaging
policy tests enforce. See `.opengrep/README.md` for why the filename matters.
policy tests enforce.

These run from a local pre-commit hook, never in CI: CodeRabbit skips its own opengrep pass
when it sees opengrep in the workflows. Install with `pre-commit install --install-hooks`.
The hooks are filtered so a commit touching neither `src/` nor the rules costs nothing. See
`.opengrep/README.md` for that and for why the filename matters.

`zizmor` audits the workflows themselves (permissions, injection, unpinned actions) in the
`Workflow audit` CI job. Run it locally with `uvx --native-tls zizmor .`, which is the
scope the CI job audits: the repo root, so `dependabot.yml` is included, not only
`.github/workflows/`. Export `GH_TOKEN` to add the online audits CI also runs.

`rust-toolchain.toml` pins the toolchain, but a `RUSTUP_TOOLCHAIN` environment variable
overrides it. Check that variable before blaming a build failure on the code.
Expand Down Expand Up @@ -153,3 +163,21 @@ Every interface also yields the two zero-index boundary rows RFC 2863 requires.
- net-snmp `local/snmp-bridge-mib`, Perl AgentX subagent reading `/sys/class/net`,
BRIDGE-MIB only.
- `snmp_rust_agent`, `sunt`.

## Agent skills

### Issue tracker

Issues live as GitHub issues in this repo, driven through the `gh` CLI. External pull
requests are treated as a request surface and triaged alongside issues. See
`docs/agents/issue-tracker.md`.

### Triage labels

The five canonical roles, each label string equal to its name. See
`docs/agents/triage-labels.md`.

### Domain docs

Single-context: `CONTEXT.md` and `docs/adr/` at the repo root. Neither exists yet, and the
skills proceed silently rather than scaffolding them. See `docs/agents/domain.md`.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ cargo fmt --check
```

`python3 packaging/test_policy.py` checks the release gate, push triggers, and
service restart policy. It requires PyYAML 6.0.3. The shared checks workflow runs it.
service restart policy. It requires PyYAML 6.0.3, pre-commit 4.5.1, `gh`, `jq`, and Bash.
The shared checks workflow runs it.

`tests/session.rs` runs the actual binary against a UnixListener. The master
uses real AgentX PDUs. A fixture executable supplies `ip` output without
Expand Down
51 changes: 51 additions & 0 deletions docs/agents/domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Domain Docs

How the engineering skills should consume this repo's domain documentation when exploring the codebase.

## Before exploring, read these

- **`CONTEXT.md`** at the repo root, or
- **`CONTEXT-MAP.md`** at the repo root if it exists: it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
- **`docs/adr/`**: read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.

If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.

## File structure

Single-context repo (most repos):

```
/
├── CONTEXT.md
├── docs/adr/
│ ├── 0001-event-sourced-orders.md
│ └── 0002-postgres-for-write-model.md
└── src/
```

Multi-context repo (presence of `CONTEXT-MAP.md` at the root):

```
/
├── CONTEXT-MAP.md
├── docs/adr/ ← system-wide decisions
└── src/
├── ordering/
│ ├── CONTEXT.md
│ └── docs/adr/ ← context-specific decisions
└── billing/
├── CONTEXT.md
└── docs/adr/
```

## Use the glossary's vocabulary

When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.

If the concept you need isn't in the glossary yet, that's a signal: either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).

## Flag ADR conflicts

If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:

> _Contradicts ADR-0007 (event-sourced orders), but worth reopening because…_
45 changes: 45 additions & 0 deletions docs/agents/issue-tracker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Issue tracker: GitHub

Issues and specs for this repo live as GitHub issues. Use the `gh` CLI for all operations.

## Conventions

- **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
- **Read an issue**: `gh issue view <number> --json number,title,body,labels,comments --jq '...'`. `--comments` prints human-readable output, so it cannot feed `jq`; use `--json` for structured reads.
- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
- **Comment on an issue**: `gh issue comment <number> --body "..."`
- **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
- **Close**: `gh issue close <number> --comment "..."`

Infer the repo from `git remote -v`; `gh` does this automatically when run inside a clone.

## Pull requests as a triage surface

**PRs as a request surface: yes.** _(External pull requests are treated as feature requests and run through the same labels and states as issues; `/triage` reads this flag. Set to `no` to exclude them.)_

When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents:

- **Read a PR**: `gh pr view <number> --comments` and `gh pr diff <number>` for the diff.
- **List external PRs for triage**: `gh pr list --json` has no `authorAssociation` field and fails with "Unknown JSON field". Use the REST endpoint, which exposes `author_association`: `gh api "repos/<owner>/<repo>/pulls?state=open" --paginate --slurp | jq '[.[][] | select(.author_association | IN("CONTRIBUTOR","FIRST_TIME_CONTRIBUTOR","NONE")) | {number, title, author: .user.login, author_association}]'` (drop `OWNER`/`MEMBER`/`COLLABORATOR`).
- **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`.

GitHub shares one number space across issues and PRs, so a bare `#42` may be either: resolve with `gh pr view 42` and fall back to `gh issue view 42`.

## When a skill says "publish to the issue tracker"

Create a GitHub issue.

## When a skill says "fetch the relevant ticket"

Run `gh pr view <number> --comments` and fall back to `gh issue view <number> --comments`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Wayfinding operations

Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.

- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`.
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
- **Blocking**: GitHub's **native issue dependencies**, the canonical, UI-visible representation. Add an edge with `gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<blocker-db-id>`, where `<blocker-db-id>` is the blocker's numeric **database id** (`gh api repos/<owner>/<repo>/issues/<n> --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only, the live gate). Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
- **Frontier query**: read the map's children in order with `gh issue view <map> --json subIssues`, then check each child's `gh issue view <n> --json state,blockedBy,assignees,body`; skip any that is closed, has an open entry in `blockedBy`, or has an assignee. The first eligible child in map order wins. Do not use `gh issue list`, which returns the 30 most recent issues by default and does not preserve map order. Where sub-issues are unavailable, walk the task list in the map body in the same order. Read each child's `body` regardless of how the children were listed: dependencies and sub-issues are separate features, so a repository can list children natively while recording blockers as a `Blocked by: #<n>` line, and such a child has an empty `blockedBy`, so treat an open issue named in `body` as a blocker.
- **Claim**: `gh issue edit <n> --add-assignee @me`, the session's first write.
- **Resolve**: `gh issue comment <n> --body "<answer>"`, then `gh issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
18 changes: 18 additions & 0 deletions docs/agents/triage-labels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Triage Labels

The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.

| Label in mattpocock/skills | Label in our tracker | Meaning |
| -------------------------- | -------------------- | ---------------------------------------- |
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
| `needs-info` | `needs-info` | Waiting on reporter for more information |
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
| `ready-for-human` | `ready-for-human` | Requires human implementation |
| `wontfix` | `wontfix` | Will not be actioned |

When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.

Edit the right-hand column to match whatever vocabulary you actually use.

`wontfix` already existed in this repo, so it is reused rather than recreated. The other
four were created for these roles.
Loading