Skip to content

Make it easier to drop the harness into an existing project #12

Description

@rxdt

Problem

The harness works as a template or has to be hand-wired into existing projects

  • Check commands are hardcoded in: harness/gate.py (COMMIT_CHECKS/FULL_CHECKS) and .github/workflows/ci.yml, with a parity test policing the drift instead of eliminating drift.
  • pylint src harness, --cov-fail-under=100, and whole-repo ruff/complexipy/semgrep make most pre-existing repo fails day one -- lots for agents to fix before they can commit, not alway desired.
  • Every check runs via uv run --no-sync, assuming a uv-managed synced venv.
  • Git hooks hardcode .venv/bin/harness and harness install silently overwrites core.hooksPath killing a host's existing hooks
  • The forbidden-paths policy is duplicated as prose in docs/PROMPT.md and AGENTS.md, allowing for potential drift from gate.py.

Design

  • [tool.ralph] in pyproject.toml becomes the single place humans set policy.
  • gate.py reads it at runtime
  • absent/empty config = today's exact strict behavior. Defaults stay as constants in gate.py, which also carry the safety minimum. pyproject.toml itself is always forbidden to agents, under any config. CI and hooks stop carrying policy, they just invoke harness gate.
[tool.ralph]
# each key REPLACES its default wholesale; absent key = strict default
forbidden-dirs = [...]
forbidden-files = [...]
forbidden-patterns = [...]
push-branch = true          # false: pre-push gate blocks agent pushes
prompt = "docs/PROMPT.md"   # what `harness run` feeds the agent

[tool.ralph.commit-checks]
"ruff lint" = ["uvx", "ruff", "check", "src/mypkg"]

[tool.ralph.full-checks]
pytest = ["poetry", "run", "pytest", "--cov=src/mypkg", "--cov-fail-under=100"]

[tool.ralph.roles.frontend]      # per-agent scopes; globals always apply on top
allowed = ["frontend/*"]
forbidden = ["backend/*"]

Under RALPH_LOOP the config is read from HEAD (git show HEAD:pyproject.toml), not the working tree so an agent editing pyproject without staging it cannot weaken the gate. Malformed config is not validated, it fails loudly (uncaught exception, hook blocks).

Three supported forms:

  1. Greenfield template git clone harness install <name> byte-identical to today.
  2. Drop-in uvx ralph-harness install in an existing repo. Writes only missing files, never touches [project]/version/tool tables, chains existing hooks.
  3. PyPI dependency. pip install ralph-harness: harness on PATH. Ships inside the wheel.

Tasks

  • Collapse ci.yml to one step uv run --no-sync harness gate (keep dependency-review, uv sync, color env).
  • Delete the two parity tests, add: ci.yml invokes harness gate (or vice versa) + contains no direct tool invocations.
  • gate.py reads [tool.ralph] for one ralph_policy(repo) function (inline tomllib, no config module/dataclass/validation)
  • HEAD-read under RALPH_LOOP with CalledProcessError fallback to defaults.
  • Casefold config sets
  • tuple the dirs (startswith)
  • union {"pyproject.toml"} floor
  • push-branch enforcement in run_gate
  • containment ejects staged paths outside the role's allowed/inside its forbidden (fnmatch globs)
  • unknown role fails loudly
  • Hooks become chaining dispatchers: resolve .venv/bin/harness else PATH harness, run with < /dev/null, then if [ -x "$(git rev-parse --git-common-dir)/hooks/<name>" ]; then exec ...; fi.
  • harness install command extended (name kept, NAME optional).
  • Current greenfield template [project].name == "ralph-harness"
  • Drop-in branch: write-if-absent the full loop contract (.githooks/*, docs/PROMPT.md, AGENTS.md, ralph_preferences.py, docs/plan.md, docs/specs/base.md, docs/PROJECT_STATUS.md). Append a real [tool.ralph] header + commented examples (tomllib to detect, plain text append to write. Idempotent, byte-diff = block only)
  • smart suggestions from a one-time read-only peek (poetry.lock, poetry-run pytest example, package dirs for lint paths) as comments only
  • wire_hooks never clobbers a foreign core.hooksPath (prints invoke lines, prints a repo: local block when .pre-commit-config.yaml exists).
  • Scaffold sources resolve repo-first, else importlib.resources (wheel force-include data doesn't exist on disk in editable installs). Wheel: exclude harness/tests, add ralph-harness script alias.
  • Host-editable preferences: install copies harness/preferences.py repo-root ralph_preferences.py, gate loads it via importlib.util.spec_from_file_location, filename joins default FORBIDDEN_FILES.
  • harness run --prompt <string|path> --role <name> + graceful "need a prompt" exit 2.
  • Policy footer injection: run() appends the live policy (forbidden sets, push-branch state, role scope) from ralph_policy() to the prompt; delete driftable hardcoded lists from docs/PROMPT.md and AGENTS.md, agents see exactly what the gate enforces.
  • publish.yml guard if github.repository == 'rxdt/py_ralph_frame'.

Non-goals

No config module/dataclass/validation layer.
No merge/extend config semantics (wholesale replace only).
No auto-ratchet or baseline files.
No copier/cruft.
No runtime package-manager detection.
No hook renaming/backups.
No --force flags.
No CLI coverage override (100 stays the default and hosts set their own number in their own toml).

Acceptance

Template with no [tool.ralph]: every dispatched argv identical to today's constants (pinned by tests).
Drop-in rehearsal: build wheel to poetry-shaped scratch repo with a pre-existing hook to uvx --from dist/*.whl ralph-harness install to commit runs harness-then-host-hook
[tool.ralph] override honored
RALPH_LOOP=1 ejects a staged pyproject edit while honoring HEAD policy
loop commit, push survives

  • PyPI rehearsal: pip install wheel in a scratch venv to install scaffolds from package data to gate runs
  • harness gate green (100% coverage, complexipy ≤ 10, all ruff caps) after every task.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions