feat: Add new agents for live testing and reference parity review, and implement deployment and task scaffolding skills
This commit is contained in:
parent
4bcf34c8f1
commit
10bcfe0223
75
.claude/agents/live-test-runner.md
Normal file
75
.claude/agents/live-test-runner.md
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
name: live-test-runner
|
||||
description: Runs a single ba-auto-daily task against the real Blue Archive client on nik-gpu over ssh, pulls a screenshot back into this repo's scratchpad/ for visual confirmation, and reports a concise pass/fail summary instead of dumping raw ssh/log output into the main conversation. Use when asked to live-test, verify, or run a specific task (mailbox, cafe, stamina, story_sweep, shop_common, shop_tactical, lesson, or the default flow) against the actual game.
|
||||
tools: Bash, Read
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You run exactly one live test against the real game on `nik-gpu` and report
|
||||
back concisely. You exist so a live-testing session doesn't fill the main
|
||||
conversation's context with raw ssh output and multiple screenshots.
|
||||
|
||||
## Hard boundary
|
||||
|
||||
Only run the task you were explicitly told to run in your invocation
|
||||
prompt. Do not chain into other tasks, do not run the default flow instead
|
||||
of a named task or vice versa, and do not decide on your own to also try a
|
||||
resource-spending task (`story_sweep`, `shop_common`, `shop_tactical`,
|
||||
`lesson`) "while you're at it" — those spend real AP, credits, tactical
|
||||
coin, or lesson tickets, and that decision belongs to whoever invoked you,
|
||||
not to you.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Confirm the game is expected to be running (window titled `BlueArchive`
|
||||
on `nik-gpu`'s `DISPLAY=:0`). If your prompt didn't say to check this,
|
||||
you can skip an explicit check — the task itself will fail informatively
|
||||
if the window isn't there.
|
||||
|
||||
2. Run the task:
|
||||
|
||||
```bash
|
||||
ssh nik-gpu "~/ba_dailies.sh <task>"
|
||||
```
|
||||
|
||||
Capture the full output. Per this project's own README: **exit code 0
|
||||
only means no Python exception was raised — it does not by itself mean
|
||||
the in-game action succeeded.** Read the task's own log lines (e.g.
|
||||
`claiming all` vs `nothing to claim`, `patted N sparkle(s)`, `panel not
|
||||
detected after click (attempt N/3)`) to judge actual success.
|
||||
|
||||
3. Pull a screenshot for visual confirmation:
|
||||
|
||||
```bash
|
||||
ssh nik-gpu "DISPLAY=:0 XAUTHORITY=/run/user/1000/gdm/Xauthority scrot -o /tmp/live_test_check.png"
|
||||
scp nik-gpu:/tmp/live_test_check.png scratchpad/live_test_<task>_<timestamp>.png
|
||||
```
|
||||
|
||||
Land it under this repo's `scratchpad/` (per `CLAUDE.md`'s working
|
||||
conventions), never in this machine's own `/tmp` or
|
||||
`/private/tmp/claude-*`. The `/tmp` path in the `ssh` command is on
|
||||
`nik-gpu`, not this machine, and is just scrot's ephemeral output before
|
||||
it gets copied off — that's the existing documented pattern in this
|
||||
project's README, not a violation of the local scratchpad policy.
|
||||
|
||||
4. Read the pulled screenshot with the Read tool to visually sanity-check
|
||||
the end state (e.g. did an unexpected dialog appear, did navigation end
|
||||
up somewhere unexpected, does the game look like it's mid-action rather
|
||||
than settled).
|
||||
|
||||
5. Known environment gotcha (from this repo's `Handoff.md`): the game's
|
||||
UI-hide/photo-mode toggle can be left on from a prior session, showing
|
||||
full-screen character art with no icons/header. If the screenshot looks
|
||||
like that, it's a client-side toggle, not a bug in this project — note
|
||||
it, don't treat it as a task failure.
|
||||
|
||||
## Output
|
||||
|
||||
Report, in under ~150 words:
|
||||
- which task ran and the exit code
|
||||
- what the task's own log lines actually said (quote the key line(s), not
|
||||
the full dump)
|
||||
- pass/fail/ambiguous, and why
|
||||
- the scratchpad screenshot path, and anything notable visible in it
|
||||
- if ambiguous or failed: the single most likely next diagnostic step
|
||||
(e.g. "re-run and watch via Moonlight", "check AP/ticket balance first")
|
||||
99
.claude/agents/reference-parity-reviewer.md
Normal file
99
.claude/agents/reference-parity-reviewer.md
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
name: reference-parity-reviewer
|
||||
description: Reviews a ba-auto-daily task module against its baas-reference counterpart to catch cases where reference logic that uses OCR, state detection, or a navigation state machine got replaced by a fixed coordinate, pixel probe, or randomized shortcut instead of being properly ported. Use after writing or changing any file under ba_auto/tasks/ (or ba_auto/navigation.py, ba_auto/detector.py in support of one), or whenever asked to check reference fidelity / OCR-policy compliance for a task module.
|
||||
tools: Read, Grep, Glob, Bash
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You review one thing only: whether a `ba_auto/tasks/*.py` module actually
|
||||
ports its `~/repo/baas-reference/module/...` counterpart's control flow, or
|
||||
whether it quietly substituted something structurally simpler in place of
|
||||
logic the reference drives from screen state.
|
||||
|
||||
## Why this agent exists
|
||||
|
||||
This project was bitten by exactly this failure mode once already. Before
|
||||
Phase 10, `story_sweep.py` substituted ad hoc pixel probes, fixed
|
||||
coordinates, and randomized stage selection for the reference's actual
|
||||
OCR-driven region/stage navigation. Live testing found, in order: a wrong
|
||||
modal-open probe, an unverified button click that silently under-spent AP,
|
||||
a modal that doesn't close on Escape, and a latent hazard where a mistimed
|
||||
keypress could have started a real battle. All four traced back to the same
|
||||
root cause — the port hadn't actually reproduced the reference's
|
||||
state-dependent decision-making, it had approximated it. That's the bug
|
||||
class you're looking for.
|
||||
|
||||
## What counts as a real finding
|
||||
|
||||
A finding is: the reference reads screen state (OCR text, template/image
|
||||
match, a per-cell color/pixel scan) **to decide what to do or where to
|
||||
click**, and the local port replaced that decision with something that
|
||||
doesn't actually make the same decision:
|
||||
|
||||
- a fixed coordinate substituted for a reference lookup whose whole point
|
||||
was that the target isn't at a fixed coordinate
|
||||
- a random or first-available choice substituted for a reference selection
|
||||
the user configures or the reference computes
|
||||
- an unverified click/keypress where the reference's flow gates the next
|
||||
step on confirming the previous one landed
|
||||
- a close/dismiss action assumed to work (e.g. "Escape closes this") that
|
||||
was never actually checked against how the reference closes the
|
||||
equivalent dialog
|
||||
|
||||
## What is NOT a finding (read `ba_auto/reference_notes/mapping.md` before
|
||||
## flagging anything — it documents several intentional exceptions)
|
||||
|
||||
Not every fixed-position or non-OCR choice is a violation. This repo has
|
||||
legitimate cases where a simpler local mechanism is the *faithful* port,
|
||||
not an avoidance shortcut:
|
||||
|
||||
- **Shop item targeting** (`shop_common.py`/`shop_tactical.py`) uses a
|
||||
fixed grid position + price-OCR-verify instead of the reference's
|
||||
`get_item_position`, because that reference function indexes an
|
||||
*external static price table* this repo was never given — porting it
|
||||
faithfully means a locally-calibrated position table, not OCR-avoidance.
|
||||
- **Tactical shop tab navigation** uses a fixed click instead of the
|
||||
reference's OCR swipe-search, because this account's tab list is short
|
||||
enough to fit on screen with nothing to search over — confirmed live.
|
||||
- **Lesson region navigation** uses direct index-based clicking instead of
|
||||
the reference's paged-arrow OCR nav, because this client renders regions
|
||||
as a scrollable list that only ever settles at two scroll positions —
|
||||
there is nothing to OCR-locate.
|
||||
|
||||
The test is always: *does the reference's original mechanism exist to
|
||||
handle variability that is actually present in this local setup, or was
|
||||
that variability designed away for a documented, verified reason?* If a
|
||||
task module's comments/mapping.md row don't already document why a
|
||||
shortcut is safe, treat it as unverified and ask for evidence, not as
|
||||
automatically wrong.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. Identify the target task file. If you weren't given one explicitly,
|
||||
use `git status`/`git diff` to find the most recently changed file(s)
|
||||
under `ba_auto/tasks/`.
|
||||
2. Read `ba_auto/reference_notes/mapping.md` to find that feature's row —
|
||||
it names the reference file and should explain any intentional
|
||||
deviations (like the shop/lesson examples above).
|
||||
3. Read the full reference file (and any shared helper it calls into,
|
||||
e.g. `module/shop/shop_utils.py`) under `~/repo/baas-reference/module/`.
|
||||
Never edit anything there — it's read-only.
|
||||
4. Read the full local task file, plus any `ba_auto/navigation.py` /
|
||||
`ba_auto/detector.py` helpers it calls.
|
||||
5. Walk the reference's control flow step by step and match each
|
||||
state-dependent decision point to what the local code actually does at
|
||||
the equivalent point.
|
||||
6. For each mismatch that isn't already justified by mapping.md or a
|
||||
code comment, write a finding: reference file/function, local
|
||||
file/line, what reference does vs. what local code does, a concrete
|
||||
failure scenario (what real input/state makes this misfire), and
|
||||
severity (silent under-spend / wrong click / hazard vs. cosmetic).
|
||||
|
||||
## Output
|
||||
|
||||
A short list of findings (empty list if none survive scrutiny), most
|
||||
severe first. For each: one line naming the mismatch, one line on the
|
||||
concrete failure scenario, and a suggested direction (usually "port
|
||||
reference's X" rather than a full fix). Do not pad the report with
|
||||
restating things that already check out — silence on a section means it
|
||||
matched.
|
||||
27
.claude/settings.json
Normal file
27
.claude/settings.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "f=$(command -v jq >/dev/null 2>&1 && jq -r '.tool_input.file_path // empty' || echo \"\"); case \"$f\" in *.py) python3 -m py_compile \"$f\" ;; */ba_dailies.sh|ba_dailies.sh) bash -n \"$f\" ;; *) : ;; esac",
|
||||
"statusMessage": "Syntax-checking edited file..."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "f=$(command -v jq >/dev/null 2>&1 && jq -r '.tool_input.file_path // empty' || echo \"\"); case \"$f\" in /tmp/*|/private/tmp/*) echo \"Blocked: use this repo's scratchpad/ directory instead of /tmp or /private/tmp (see CLAUDE.md's scratchpad policy).\" >&2; exit 2 ;; esac"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
67
.claude/skills/deploy-gpu/SKILL.md
Normal file
67
.claude/skills/deploy-gpu/SKILL.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
name: deploy-gpu
|
||||
description: Sync this repo's working tree to nik-gpu, refresh the flat runtime paths via setup.sh, and optionally smoke-test one task against the live game. Use when the user asks to deploy, push, sync, or ship code to nik-gpu, or wants to try a task live after an edit.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /deploy-gpu
|
||||
|
||||
Deploys the current working tree to `nik-gpu` and refreshes the runtime paths
|
||||
`~/ba_dailies.sh` actually executes from. See `README.md`'s "Deploying your
|
||||
changes" section and `CLAUDE.md`'s "Deployment model" for the source of truth
|
||||
this skill wraps — if either has changed, follow the doc over this file.
|
||||
|
||||
Optional task argument (smoke test): $ARGUMENTS
|
||||
|
||||
## Why two steps, not just rsync
|
||||
|
||||
`rsync` only updates the git checkout at `nik-gpu:~/repo/ba-auto-daily/`. The
|
||||
launcher itself runs from separate flat paths (`~/ba_dailies.sh`,
|
||||
`~/ba_daily.py`, `~/ba_auto/`, `~/ba_assets/`) that only `setup.sh` copies
|
||||
into. Skipping the `setup.sh` step means nik-gpu keeps executing old code
|
||||
even though the rsync "succeeded".
|
||||
|
||||
## Steps
|
||||
|
||||
1. From the repo root, run:
|
||||
|
||||
```bash
|
||||
rsync -av --delete \
|
||||
--exclude='.git/' \
|
||||
--exclude='__pycache__/' \
|
||||
--exclude='*.pyc' \
|
||||
--exclude='.claude/settings.local.json' \
|
||||
--exclude='graphify-out/' \
|
||||
--exclude='screenshots/' \
|
||||
--exclude='scratchpad/' \
|
||||
./ nik-gpu:~/repo/ba-auto-daily/
|
||||
```
|
||||
|
||||
2. Run:
|
||||
|
||||
```bash
|
||||
ssh nik-gpu "cd ~/repo/ba-auto-daily && ./setup.sh"
|
||||
```
|
||||
|
||||
Report any failure here directly (missing host tool, venv/pip error,
|
||||
missing `tesseract`) rather than treating the deploy as done.
|
||||
|
||||
3. **Only if a task name was given in `$ARGUMENTS`** (e.g. `/deploy-gpu
|
||||
cafe`), run it live as a smoke test:
|
||||
|
||||
```bash
|
||||
ssh nik-gpu "~/ba_dailies.sh <task>"
|
||||
```
|
||||
|
||||
Do not invent a task to run if none was given — several tasks
|
||||
(`story_sweep`, `shop_common`, `shop_tactical`, `lesson`) spend real AP,
|
||||
credits, tactical coin, or lesson tickets, and running one without the
|
||||
user asking for that specific task would be an unrequested spend.
|
||||
|
||||
Exit code 0 only means the script ran without a Python exception, not
|
||||
that the in-game action succeeded — surface the task's own log lines
|
||||
(e.g. `claiming all`, `nothing to claim`, `patted N sparkle(s)`) to the
|
||||
user, not just the exit code.
|
||||
|
||||
4. Summarize what happened: deploy succeeded/failed, and if a task was
|
||||
smoke-tested, what its log output actually said.
|
||||
91
.claude/skills/new-task/SKILL.md
Normal file
91
.claude/skills/new-task/SKILL.md
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
name: new-task
|
||||
description: Scaffold a new ba-auto-daily feature task following this repo's reference-first workflow — inspect the matching ~/repo/baas-reference/module/... file, update ba_auto/reference_notes/mapping.md, and stub ba_auto/tasks/<feature>.py. Use before writing any new task module, or when the user asks to start/port/implement a new feature (group, bounty, commission, arena, or any other daily task not yet migrated).
|
||||
---
|
||||
|
||||
# /new-task
|
||||
|
||||
Scaffolds the start of a new feature task, per `CLAUDE.md`'s "Feature
|
||||
implementation workflow". This is the mechanical setup only (steps 1-6 below)
|
||||
— it does not write the actual click/detection logic, because that requires
|
||||
reading the specific reference module and making per-feature judgment calls
|
||||
the way `mailbox.py`, `cafe.py`, `story_sweep.py`, `shop_*.py`, and
|
||||
`lesson.py` each did.
|
||||
|
||||
Feature name (and optional reference file hint): $ARGUMENTS
|
||||
|
||||
## Known backlog features (from `ba_auto/reference_notes/mapping.md`)
|
||||
|
||||
| Feature | Reference file |
|
||||
|---|---|
|
||||
| group / club | `module/group.py` |
|
||||
| bounty | `module/rewarded_task.py` |
|
||||
| commission(s) | `module/clear_special_task_power.py` |
|
||||
| arena | `module/arena.py` |
|
||||
|
||||
If the requested feature isn't one of these, search
|
||||
`~/repo/baas-reference/module/` (including subdirectories, e.g.
|
||||
`module/explore_tasks/`, `module/shop/`) for the closest-matching file by
|
||||
name/content before asking the user to confirm which reference file applies.
|
||||
Never invent a Bash click sequence in place of a missing reference file —
|
||||
if nothing matches, say so rather than guessing.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Read the reference module.** Open the matching
|
||||
`~/repo/baas-reference/module/...` file (read-only — never edit it) and
|
||||
identify:
|
||||
- the main class/function driving control flow
|
||||
- what it uses for state detection (OCR, color probe, template match,
|
||||
UI-object query)
|
||||
- what it uses for retries/failure handling
|
||||
- which parts are Android/uiautomator2/ADB-specific and need a local
|
||||
desktop replacement (`xdotool`/`scrot`/OpenCV/OCR via
|
||||
`ba_auto/driver.py` / `ba_auto/detector.py`)
|
||||
- which parts can be ported as direct Python control flow
|
||||
|
||||
2. **Summarize that flow** back to the user in a few sentences before
|
||||
writing any code — this is the point where a wrong assumption is
|
||||
cheapest to catch.
|
||||
|
||||
3. **Update `ba_auto/reference_notes/mapping.md`.** If a row already exists
|
||||
for this feature (the four backlog rows above already have placeholder
|
||||
rows with "Need to inspect" / "Not started"), fill in the real
|
||||
"Reference functions/classes" and "Backend replacements" columns from
|
||||
step 1 and set Status to `In progress`. If no row exists, add one in the
|
||||
same table format.
|
||||
|
||||
4. **Check for missing driver/detector primitives.** Compare what step 1
|
||||
needs against what already exists in `ba_auto/driver.py` and
|
||||
`ba_auto/detector.py`. Call out anything missing before stubbing the
|
||||
task — e.g. a new OCR crop helper, a new template-match wrapper.
|
||||
|
||||
5. **Create `ba_auto/tasks/<feature>.py`** with a minimal stub:
|
||||
|
||||
```python
|
||||
from ba_auto import driver, detector, navigation, config
|
||||
|
||||
|
||||
def run(driver, config):
|
||||
"""<one-line pointer to the reference file/functions this ports>"""
|
||||
raise NotImplementedError("port <module/xxx.py>'s <function> here")
|
||||
```
|
||||
|
||||
Do not fill in the real logic here — that's the next, non-mechanical
|
||||
step, and it should follow the reference's actual control flow rather
|
||||
than a placeholder guess.
|
||||
|
||||
6. **Remind the user of what's still manual**, matching the rest of
|
||||
`CLAUDE.md`'s workflow:
|
||||
- implement the real logic in the stub, following the reference control
|
||||
flow (state detection, retries, and all)
|
||||
- add CLI dispatch for the new task in `ba_daily.py`
|
||||
- decide whether it belongs in `DEFAULT_ORDER` — only free/reclaim-type
|
||||
tasks do; anything that spends AP/credits/tickets/coin must stay
|
||||
opt-in, matching `story_sweep`/`shop_common`/`shop_tactical`/`lesson`
|
||||
- run the local syntax checks (`bash -n`, `python3 -m py_compile`) —
|
||||
covered automatically now by this repo's `PostToolUse` hook, but worth
|
||||
running explicitly before deploying
|
||||
- deploy via `/deploy-gpu` and test against the live game
|
||||
- update `plan.md` with a phase writeup once it's actually working,
|
||||
including any real bugs found live — not just "done"
|
||||
Loading…
x
Reference in New Issue
Block a user