chore: added robomp workspace, ignored fallow deps, cleaned up exports

- Added `python/robomp` as a workspace package alongside its nested `web` sub-package.
- Added `ignoreDependencies` in `.fallowrc.jsonc` for deps used via bin or in nested workspaces.
- Removed unused `clearTriggerStatus` export and unexported `tick` and `ByModelToggle`.
This commit is contained in:
can1357
2026-05-17 01:44:13 +02:00
parent 6120adbde2
commit 3061c47a32
10 changed files with 53 additions and 99 deletions
+1 -1
View File
@@ -165,7 +165,7 @@ ROBOMP_BIND_PORT=8080
# --- oh-my-pi source location (host side) ---
# =============================================================================
# robomp lives inside the oh-my-pi monorepo at `python/robomp/`. The default
# `bun run pi-artifacts` builds the parent monorepo (`../..`) as its docker
# `bun run robomp:pi-artifacts` builds the parent monorepo (`../..`) as its docker
# build context, and `docker-compose.yml` mounts that same path read-only at
# `/work/pi` inside the container. Override `PI_ROOT` only if you want to point
# the build/mount at a different oh-my-pi checkout.
+23 -23
View File
@@ -23,48 +23,48 @@ Webhook → durable queue → async dispatcher → per-issue git worktree → om
- `src/robomp/prompts/` — Mustache-style `{{var}}` templates loaded by `persona.py` via `@cache` and `importlib.resources`. Shipped as package data (`pyproject.toml` `package-data`).
- `tests/` — pytest suite. `test_worker_smoke.py` is gated on `ROBOMP_INTEGRATION=1`.
- `data/` — runtime state (sqlite + WAL, `workspaces/`, `logs/`). Never committed.
- `/work/pi/Dockerfile` — produces `oh-my-pi/artifacts:dev` (pi-natives `.node` + omp-rpc wheel). Built once per pi-source change via `bun run pi-artifacts`; roboomp's runtime image consumes it via `COPY --from=`.
- `/work/pi/Dockerfile` — produces `oh-my-pi/artifacts:dev` (pi-natives `.node` + omp-rpc wheel). Built once per pi-source change via `bun run robomp:pi-artifacts`; roboomp's runtime image consumes it via `COPY --from=`.
## Development Commands
Task runner is now `bun` against the root `package.json` (workspaces = `["web"]`). `just` is gone; every recipe lives in the root `scripts` block. Local venv (no docker): `bun run install:py` runs `pip install -e '.[dev]'`. From there:
Task runner is `bun` against the **monorepo root** `package.json`. roboomp itself no longer ships a `package.json`; every recipe lives at the root under the `robomp:*` namespace. Local venv (no docker): `bun run robomp:install` runs `pip install -e 'python/robomp[dev]'`. From there:
```
bun run test # pytest -x tests/
bun run test:file <PATH> # single file
bun run test:integration # ROBOMP_INTEGRATION=1, requires omp on PATH
bun run serve # python -m robomp serve on the host
bun run test:py # pytest -x python/omp-rpc/tests python/robomp/tests
bun run robomp:test:integration # ROBOMP_INTEGRATION=1, requires omp on PATH
bun run robomp:serve # python -m robomp serve on the host
```
Docker inner loop:
```
bun run build # pi-artifacts (if pi changed) + docker compose build
bun run dev # build + up -d + follow logs
bun run up / down / restart / logs / sh
bun run rebuild # docker compose build --no-cache
bun run robomp:build # pi-artifacts (if pi changed) + docker compose build
bun run robomp:dev # build + up -d + follow logs
bun run robomp:up / robomp:down / robomp:restart / robomp:logs
bun run robomp:rebuild # docker compose build --no-cache
bun run robomp:reset # `down -v` + drop the pi-artifacts image
```
Frontend (Vite + SolidJS, in `web/`):
Frontend (Vite + SolidJS, in `web/` — still a bun workspace):
```
bun run web:dev # vite dev server with proxy to :8080
bun run web:build # produce src/robomp/static/ bundle
bun run web:typecheck # tsc --noEmit
bun run robomp:web:dev # vite dev server with proxy to :8080
bun run robomp:web:build # produce src/robomp/static/ bundle
bun --cwd=python/robomp/web run typecheck # tsc --noEmit
```
In-container CLI (`robomp` console script → `robomp.cli:main`):
In-container CLI (`robomp` console script → `robomp.cli:main`): no root aliases — invoke directly:
```
bun run triage owner/repo#N # full pipeline against a live issue
bun run replay <delivery_id> # re-enqueue a stored webhook
bun run issue-status # tabular dump of issues table
bun run cleanup owner/repo#N # force workspace removal + state=abandoned
docker compose --project-directory python/robomp exec robomp robomp triage owner/repo#N
docker compose --project-directory python/robomp exec robomp robomp replay <delivery_id>
docker compose --project-directory python/robomp exec robomp robomp status
docker compose --project-directory python/robomp exec robomp robomp cleanup owner/repo#N
```
HTTP/sqlite inspection: `bun run healthz`, `bun run readyz`, `bun run events [N]`, `bun run issues [N]`, `bun run sqlite`, `bun run sql "<SQL>"`, `bun run tool-calls owner/repo#N`, `bun run stuck`. Webhook smoke: `bun run ping`. Danger: `bun run wipe-workspaces`, `bun run nuke-data`, `bun run reset`.
HTTP / sqlite / webhook inspection is unaliased — use `curl http://localhost:${ROBOMP_BIND_PORT:-8080}/{healthz,readyz,events,issues}` and `docker compose --project-directory python/robomp exec robomp sqlite3 /data/robomp.sqlite` directly.
Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (config in `pyproject.toml`). `bun run lint` checks both; `bun run fix` rewrites both. `bun run lint:ts` / `bun run lint:py` / `bun run fix:ts` / `bun run fix:py` scope to one language. `bun run typecheck` runs `tsc --noEmit` against `web/`. Run before committing non-trivial changes; CI is not yet wired up.
Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (config in `pyproject.toml`). Root recipes cover both languages — `bun run lint` / `bun run fix` apply to the whole monorepo including roboomp. `bun run lint:py` / `bun run fix:py` scope to Python only.
## Code Conventions & Common Patterns
@@ -108,7 +108,7 @@ Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (c
- **Task runner**: `bun` (root `package.json` `scripts`). Always reach for an existing `bun run` recipe before invoking `docker compose` or `pytest` directly.
- **Container runtime**: Docker Compose v2. The image embeds Bun 1.3.14 + a rustup launcher and exposes `omp` via a `/usr/local/bin/omp` shim; `ROBOMP_OMP_COMMAND=omp` should not need changing.
- **Required env** (set in `.env`, see `.env.example`): `GITHUB_WEBHOOK_SECRET`, `ROBOMP_BOT_LOGIN`, `ROBOMP_GIT_AUTHOR_NAME`, `ROBOMP_GIT_AUTHOR_EMAIL`, `ROBOMP_REPO_ALLOWLIST`, plus model knobs (`ROBOMP_MODEL`, `ROBOMP_THINKING`, optional `ROBOMP_PROVIDER`) and rate-limit / concurrency / timeout overrides. **GitHub auth is mode-exclusive**: either set `ROBOMP_GH_PROXY_URL` + `ROBOMP_GH_PROXY_HMAC_KEY` (gh-proxy mode; PAT lives only in the sidecar container — the bundled compose default), or set `GITHUB_TOKEN` directly (single-process PAT mode). `Settings._validate_proxy_or_pat` rejects a `.env` that sets both.
- **PI_ROOT resolution**: roboomp lives inside the oh-my-pi monorepo at `python/robomp/`. `bun run pi-artifacts` builds the parent monorepo (`../..`) as its docker build context, and `docker-compose.yml` mounts that same path read-only at `/work/pi`. Override `PI_ROOT` only when pointing the build/mount at a different oh-my-pi checkout. Inside the container the path is always `/work/pi`. Build invalidation stays bounded: Python-only edits in roboomp never trigger a natives recompile.
- **PI_ROOT resolution**: roboomp lives inside the oh-my-pi monorepo at `python/robomp/`. `bun run robomp:pi-artifacts` builds the parent monorepo (`../..`) as its docker build context, and `docker-compose.yml` mounts that same path read-only at `/work/pi`. Override `PI_ROOT` only when pointing the build/mount at a different oh-my-pi checkout. Inside the container the path is always `/work/pi`. Build invalidation stays bounded: Python-only edits in roboomp never trigger a natives recompile.
- **Forbidden**: no docker-in-docker, no extra service containers, no new background workers outside `WorkerPool`. The container itself is the isolation boundary; per-issue isolation is the git worktree.
## Testing & QA
@@ -121,5 +121,5 @@ Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (c
- **Isolation rules**: any test mutating env via `monkeypatch.setenv` MUST also call `reset_settings_cache()` to invalidate the `@cache`d `get_settings()`.
- **Async tests**: `test_github_client.py` and `test_host_tools.py` spin custom event loops in background threads to bridge sync-style tests with async client code. Prefer `pytest-asyncio` `auto` mode (`async def test_*`) for new tests; only fall back to the loop helpers if matching the surrounding file's style.
- **Mocking**: never patch internals; inject test doubles via `httpx.MockTransport` for HTTP and via the `db` / `tmp_path` fixtures for storage. Sandbox tests use a real local bare repo as the upstream.
- **Integration**: `tests/test_worker_smoke.py` is gated by `ROBOMP_INTEGRATION=1` (uses `pytestmark.skipif`) and needs `omp` on `PATH`. Don't enable it in default `bun run test`.
- **Integration**: `tests/test_worker_smoke.py` is gated by `ROBOMP_INTEGRATION=1` (uses `pytestmark.skipif`) and needs `omp` on `PATH`. Don't enable it in default `bun run test:py`.
- **Coverage expectation**: ~80 unit tests currently. New code with a control-flow branch needs a test covering it; new host tools need at minimum a happy path + one validation-failure path mirroring `test_host_tools.py`. Test logical behavior (assertions on observable effects in DB / HTTP requests), not literal strings or default config values.
+1 -1
View File
@@ -5,7 +5,7 @@
# Build is split across three stages:
#
# 1) pi-artifacts — pull a pre-built `oh-my-pi/artifacts:dev` image (built
# separately from /work/pi/Dockerfile, see `bun run pi-artifacts`):
# separately from /work/pi/Dockerfile, see `bun run robomp:pi-artifacts`):
# - pi_natives.linux-<arch>.node → /opt/bun/bin/ (the pi loader probes here)
# - omp_rpc-*.whl → pip install
# 2) web-builder — Bun + Vite compile the SolidJS dashboard bundle from
+7 -7
View File
@@ -63,8 +63,8 @@ $EDITOR .env
openssl rand -hex 32 # ROBOMP_GH_PROXY_HMAC_KEY
openssl rand -hex 32 # GITHUB_WEBHOOK_SECRET
bun run pi-artifacts # build oh-my-pi/artifacts:dev (one-time / on pi change)
bun run build && bun run up
bun run robomp:pi-artifacts # build oh-my-pi/artifacts:dev (one-time / on pi change)
bun run robomp:build && bun run robomp:up
curl -fsS http://localhost:8080/healthz
```
@@ -111,9 +111,9 @@ docker compose exec robomp robomp status # dump issues table
docker compose exec robomp robomp cleanup owner/repo#123 # force workspace removal, state=abandoned
```
`bun run …` shortcuts in `package.json` cover the common ones
(`bun run triage`, `bun run replay`, `bun run sql`, `bun run events`,
`bun run logs`, `bun run sh`, etc.).
`bun run robomp:…` shortcuts in the root `package.json` cover the common
lifecycle commands (`robomp:dev`, `robomp:build`, `robomp:up`, `robomp:down`,
`robomp:logs`, `robomp:restart`, `robomp:reset`).
## Tests
@@ -124,7 +124,7 @@ ROBOMP_INTEGRATION=1 pytest -x tests/test_worker_smoke.py
The integration test spawns a real `omp --mode rpc` against an
`httpx.MockTransport` GitHub and a local bare repo, so it needs `omp` on
`PATH`. `bun run test` runs the unit suite.
`PATH`. `bun run test:py` runs the unit suite.
## Security posture
@@ -186,7 +186,7 @@ The integration test spawns a real `omp --mode rpc` against an
| `refusing to push: commit author identity mismatch` | Some commit not authored as `ROBOMP_GIT_AUTHOR_*`. The error lists the offending shas; `git commit --amend --reset-author --no-edit`. |
| `refusing to push: working tree is dirty` | Uncommitted agent edits. Or just call `gh_open_pr`, which auto-commits `bun run fix` output. |
| `bun check failed before PR creation` | Fix the reported failure and retry `gh_open_pr`. |
| `Failed to load pi_natives` | Wrong arch / missing native. `bun run pi-artifacts` then `bun run build`. |
| `Failed to load pi_natives` | Wrong arch / missing native. `bun run robomp:pi-artifacts` then `bun run robomp:build`. |
| `No API key found for <provider>` | `~/.omp/agent/models.container.yml` mount missing or provider id mismatch with `ROBOMP_MODEL`. |
## Layout
+1 -1
View File
@@ -17,7 +17,7 @@ services:
context: ../..
dockerfile: python/robomp/Dockerfile
args:
# Tag of the pre-built artifacts image produced by `bun run pi-artifacts`
# Tag of the pre-built artifacts image produced by `bun run robomp:pi-artifacts`
# (sources: pi root /Dockerfile). Override per-environment as needed.
PI_ARTIFACTS_IMAGE: oh-my-pi/artifacts:dev
image: robomp:dev
-60
View File
@@ -1,60 +0,0 @@
{
"name": "robomp",
"private": true,
"type": "module",
"packageManager": "bun@1.3.14",
"description": "Self-hosted GitHub triage-and-fix bot driving oh-my-pi.",
"scripts": {
"dev": "bun run build && bun run up && bun run logs",
"build": "bun run pi-artifacts && docker compose build",
"rebuild": "docker build --no-cache -t \"${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev}\" ../.. && docker compose build --no-cache",
"pi-artifacts": "docker build -t \"${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev}\" ../..",
"clean-pi-artifacts": "docker image rm ${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev} || true",
"image-info": "docker image inspect robomp:dev --format 'size: {{.Size}} bytes layers: {{len .RootFS.Layers}} created: {{.Created}}'",
"up": "docker compose up -d",
"down": "docker compose down",
"restart": "docker compose restart robomp",
"ps": "docker compose ps",
"logs": "docker compose logs -f robomp",
"proxy-logs": "docker compose logs -f gh-proxy",
"tail": "docker compose logs --no-color --tail \"${1:-200}\" robomp",
"log-grep": "docker compose logs --no-color robomp | grep -i -- \"$1\" || true",
"sh": "docker compose exec robomp bash",
"exec": "docker compose exec robomp \"$@\"",
"triage": "docker compose exec robomp robomp triage \"$1\"",
"replay": "docker compose exec robomp robomp replay \"$1\"",
"issue-status": "docker compose exec robomp robomp status",
"cleanup": "docker compose exec robomp robomp cleanup \"$1\"",
"install:py": "pip install -e '.[dev]'",
"test": "pytest -x tests/ \"$@\"",
"test:integration": "ROBOMP_INTEGRATION=1 pytest -x tests/test_worker_smoke.py \"$@\"",
"test:file": "pytest -x \"$@\"",
"serve": "python3 -m robomp serve",
"lint": "bun run lint:ts && bun run lint:py",
"lint:ts": "biome check . --no-errors-on-unmatched",
"lint:py": "ruff check src tests && ruff format --check src tests",
"fix": "bun run fix:ts && bun run fix:py",
"fix:ts": "biome check --write --unsafe --no-errors-on-unmatched .",
"fix:py": "ruff check --fix src tests && ruff format src tests",
"fmt": "biome format --write --no-errors-on-unmatched .",
"check": "biome check . --no-errors-on-unmatched",
"typecheck": "bun --cwd=web run typecheck",
"web:dev": "bun --cwd=web run dev",
"web:build": "bun --cwd=web run build",
"web:preview": "bun --cwd=web run preview",
"web:typecheck": "bun --cwd=web run typecheck",
"healthz": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/healthz\" && echo",
"readyz": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/readyz\" && echo",
"events": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/events?limit=${1:-50}\" | python3 -m json.tool",
"issues": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/issues?limit=${1:-100}\" | python3 -m json.tool",
"sqlite": "docker compose exec robomp sqlite3 /data/robomp.sqlite",
"sql": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"$1\"",
"tool-calls": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"SELECT id, ts, tool, COALESCE(error,'ok') AS err FROM tool_calls WHERE issue_key='$1' ORDER BY id;\"",
"recent-events": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"SELECT received_at, event_type, issue_key, state, attempts FROM events ORDER BY received_at DESC LIMIT ${1:-20};\"",
"stuck": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"SELECT delivery_id, event_type, issue_key, state, attempts, started_at FROM events WHERE state IN ('queued','running') ORDER BY received_at;\"",
"ping": "bash scripts/ping.sh",
"wipe-workspaces": "rm -rf ./data/workspaces && mkdir -p ./data/workspaces",
"nuke-data": "rm -rf ./data && mkdir -p ./data",
"reset": "docker compose down -v && (docker image rm ${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev} || true)"
}
}
+1 -5
View File
@@ -29,7 +29,7 @@ export { isFetching, lastTickAt, lastTickError };
let pollHandle: number | null = null;
export async function tick(): Promise<void> {
async function tick(): Promise<void> {
setIsFetching(true);
try {
await Promise.all([refetchStatus(), refetchLogs()]);
@@ -113,7 +113,3 @@ export async function runCancel(deliveryId: string): Promise<void> {
}
void tick();
}
export function clearTriggerStatus(): void {
setTriggerStatus({ kind: "idle", text: "" });
}