Skip to content

Add test to confirm we can downgrade to older harperdb #10

Add test to confirm we can downgrade to older harperdb

Add test to confirm we can downgrade to older harperdb #10

name: Claude Mention Handler
# Responds to `@claude …` in PR comments and PR review (inline) comments.
# Claude enters the action's "agent mode": reads the commenter's request,
# uses the PR/issue as context, and can edit + commit + push. Gated to
# HarperFast org members/collaborators so external contributors can't
# trigger work against the repo.
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
concurrency:
group: claude-mention-${{ github.event.issue.number || github.event.pull_request.number }}
cancel-in-progress: false
jobs:
work:
# Belt-and-suspenders gate:
# 1. Comment must contain the trigger phrase.
# 2. Commenter must be HarperFast org OWNER / MEMBER or a repo
# COLLABORATOR (the action also performs its own write-access
# check on the actor as a fallback).
if: >-
contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'),
github.event.comment.author_association)
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
# Write access is intentional — mention mode is "do work", which
# means editing files, committing, and pushing (either to the PR's
# branch or a new claude/… branch for issue-originated asks).
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- name: Checkout
# Default shallow fetch (depth 1). The agent can commit and push on a
# shallow clone; `git log` / `git blame` aren't reached for by the
# current prompt. Bump to a deeper fetch only if we see the agent
# blocked on history lookups.
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Parse mention
# Real precision gate (the job-level `if:` is a cheap pre-filter).
# Enforces:
# 1. `@claude` must be the FIRST non-whitespace token (word-
# boundary after) — rules out `@claudette`, inline prose
# mentions ("saw @claude's fix"), and quoted replies
# (`> @claude ...`) where the reply is addressing a human.
# 2. Case-insensitive word-boundary `deep` anywhere in the body
# → escalate to Opus. Sonnet is the default.
id: mention
env:
BODY: ${{ github.event.comment.body }}
run: |
set -uo pipefail
if ! printf '%s' "$BODY" | grep -Pqz '\A\s*@claude\b'; then
echo "Comment does not start with @claude; skipping."
echo "proceed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if printf '%s' "$BODY" | grep -Piq '\bdeep\b'; then
echo "model=claude-opus-4-7" >> "$GITHUB_OUTPUT"
echo "Selected claude-opus-4-7 (deep requested)"
else
echo "model=claude-sonnet-4-6" >> "$GITHUB_OUTPUT"
echo "Selected claude-sonnet-4-6 (default)"
fi
echo "proceed=true" >> "$GITHUB_OUTPUT"
- name: Clone shared Harper skills
# Pinned to a SHA so agent behavior is reproducible across runs —
# updates require an explicit pin bump here.
if: steps.mention.outputs.proceed == 'true'
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
repository: HarperFast/skills
ref: d2db99bb37a6dde868cbc5ac81ca4146be8956fb # 1.3.0 (2026-04-16)
path: .harper-skills
- name: Setup Node.js
# Needed so the agent can run `npm ci` / `npm run <script>` when a
# specific mention actually requires it. We DON'T eagerly `npm ci`
# here — most mentions (explain, review, tiny edits) don't need
# deps, and ~35-60s install cost × every mention adds up. The
# prompt tells the agent to run `npm ci` itself before any script
# that needs dependencies.
if: steps.mention.outputs.proceed == 'true'
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: '22'
cache: 'npm'
- name: Claude (agent mode)
if: steps.mention.outputs.proceed == 'true'
id: claude-agent
uses: anthropics/claude-code-action@c3d45e8e941e1b2ad7b278c57482d9c5bf1f35b3 # v1.0.99
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
show_full_output: true
claude_args: |
--model ${{ steps.mention.outputs.model }}
--max-turns 48
# Tool allowlist is a security boundary — every entry is a
# potential RCE primitive if a prompt injection succeeds.
# Deliberately ABSENT:
# * `Bash(npx:*)` — would let an injected instruction run
# arbitrary published packages.
# Deliberately TIGHTENED:
# * `Bash(npm install)` (no-arg, not `Bash(npm install:*)`) —
# blocks `npm install @attacker/<name>`. BUT: the agent
# also has `Write`/`Edit` on package.json. A successful
# injection could add a malicious `postinstall` script
# and then invoke bare `npm install` to execute it, with
# GITHUB_TOKEN + the claude[bot] installation token
# reachable from the subprocess. This allowlist ALONE
# does not close that path — branch protection + the
# author_association gate are what actually bound blast
# radius. A future PR may add `ignore-scripts=true` via
# .npmrc and/or drop `Bash(npm install)` entirely,
# deferring installs to a separate CI job.
--allowedTools "Read,Write,Edit,Grep,Glob,mcp__github_inline_comment__create_inline_comment,Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh pr comment:*),Bash(gh pr checkout:*),Bash(gh pr create:*),Bash(gh issue view:*),Bash(gh issue comment:*),Bash(git:*),Bash(npm install),Bash(npm ci:*),Bash(npm run:*),Bash(npm test:*)"
# In agent mode the action can use the triggering comment as the
# prompt, but we inline it explicitly below to guarantee the agent
# always has PR/issue number, URL, and the commenter's exact
# request.
#
# TODO: revisit if a future claude-code-action release reliably
# forwards the triggering comment as the prompt.
prompt: |
You were invoked via an `@claude` mention on ${{ github.repository }}.
## Mention context
- Repo: ${{ github.repository }}
- Target number: #${{ github.event.issue.number || github.event.pull_request.number }}
- Target URL: ${{ github.event.issue.html_url || github.event.pull_request.html_url }}
- Target kind: ${{ github.event.issue.pull_request && 'pull request' || (github.event.pull_request && 'pull request' || 'issue') }}
- Commenter: @${{ github.event.comment.user.login }}
The commenter wrote (verbatim, including any multi-line content):
```
${{ github.event.comment.body }}
```
Start by reading the target so you have real context:
- For a PR: `gh pr view <N>` then `gh pr diff <N>` if you need
the changes.
- For an issue: `gh issue view <N>`.
Then act on the request. If the request is "review this PR",
follow the review discipline from HarperFast/ai-review-prompts
(see .github/workflows/claude-review.yml for the layered
scope this repo uses) — do NOT treat review as a code-edit task.
## Conventions
Read the repo's agent context files first (commonly
`CLAUDE.md`, `AGENTS.md`, or similar at the repo root). Their
conventions and gotchas apply to any code you write. Match
the repo's existing style rather than introducing a new one.
Harper-specific notes for code work:
- Linter is oxlint, not eslint. `npm run lint` runs oxlint.
- Primary storage is RocksDB (LMDB is the alternate via
`HARPER_STORAGE_ENGINE=lmdb`).
- TypeStrip-compatible (`erasableSyntaxOnly`). Don't use
TypeScript features that break typestrip.
- `dependencies.md` documents all npm packages; if you add
a runtime dep, add an entry there.
## Before committing
Scale validation to the kind of change you made:
- Doc-only change (only `*.md` / `README.md` / `CLAUDE.md` /
`AGENTS.md` / `dependencies.md`, or `package.json`
keyword/description edits): run `npm run format:check` and
`npm run lint`. Do NOT run `npm run build` /
`npm run test:unit` — they are not affected and waste turns.
- Code change that affects behavior: run `npm ci` first
(deps aren't pre-installed in this workflow), then
`npm run build && npm run lint && npm run format:check && npm run test:unit`.
Fix anything that fails. Integration tests
(`npm run test:integration`) are slow; run them only if
the change plausibly affects integration behavior.
When in doubt, err toward the fuller validation.
## Output
- Scope your changes to exactly what the mention asked for.
Don't refactor unrelated code.
- Commit with a descriptive message referencing the
issue/PR.
- If the request is ambiguous or you have to make a
judgment call that changes public API or semantics, post
a comment on the PR/issue explaining your interpretation
and stop — do NOT push speculative changes.
- Do NOT use REQUEST_CHANGES or APPROVE on PRs. Post
comments or push commits only.