Built as a Lemma pod and Vite/React Lemma App for the Gappy AI Hackathon, "Ship to Get Hired".
Coding agents can already write patches. The maintainer's harder question is: can I trust this patch enough to let it enter my repository?
Grappy makes that trust layer the product. It reproduces a bug with a real failing
pytestrun before writing code, fixes against that maintainer-approved oracle, records every step as an append-only audit log, asks a Lemma Agent reviewer for a second opinion, pauses for human approval, and only then opens a pull request whose body is the evidence trail.
The coding loop is useful. The audit is the moat.
- What Grappy Solves
- The Product
- Architecture
- One Change Run
- Lemma SDK Usage
- Repository Structure
- Backend Pod
- Frontend App
- Data Model
- Functions
- Workflow And Agent
- Trust, Safety, And Verification
- GitHub Connector
- Observability And Replay
- Development
- Deployment
- Demo Script
- Design Decisions
- Roadmap
Solo and small-team Python maintainers can already ask an AI coding agent to produce a patch. That is no longer the scarce part.
The remaining bottleneck is trust:
- Did the agent reproduce the bug before it changed code?
- Was the failing test supplied or approved by a maintainer?
- Did the agent localize the fault from real execution evidence?
- Did the patch actually flip the failing test from RED to GREEN?
- Did nearby regression tests stay green?
- Is there enough evidence for a reviewer to understand the change?
- Who approved the automation before it opened a pull request?
- Can the run be inspected later without asking the model to explain itself?
Grappy chooses a narrow problem statement on purpose:
Enable a maintainer to safely delegate the repair of a known Python bug while preserving trust, auditability, and human control throughout the workflow.
The goal is not fully autonomous software development. The goal is a repair workflow where every AI-generated change is reproducible, inspectable, independently validated, and explicitly approved before becoming a pull request.
| Field | Choice |
|---|---|
| User | Solo or small-team maintainer of a Python repository |
| Repository shape | Python project with pytest |
| Starting input | Repo context, issue description, and a failing pytest target or built-in demo |
| Unit of work | One repo, one bug, one governed change_run |
| Human role | Owns the merge decision and must approve before PR creation |
| Non-goal | Autonomous merge or broad multi-language repair |
Grappy is designed around one acceptance rule:
The agent may write the patch, but it does not get to write the truth it is trying to satisfy.
The truth is the failing test. For real runs, that test comes from the maintainer or a maintainer-approved source. For the built-in demo, the repository carries a known seed bug and test. Grappy's job is to move that independent oracle from RED to GREEN, not to manufacture its own definition of success.
Grappy is a governed bug-fix workflow built as:
- a Lemma pod in
grappy-pod/ - a Vite/React Lemma App in
grappy-app/ - a tested executor library in
grappy-pod/executor/ - a design and research corpus in
design-docs/andknowledge-base/
The maintainer picks a repository, describes the bug, optionally provides a failing test and target file, and starts a run. Grappy then:
- Creates a
change_runsrow. - Runs the unpatched code in a clean workspace with
pytest. - Requires a real non-zero exit code before patching.
- Localizes the likely fault using indexed repo symbols and traceback output.
- Drafts a plan and records it.
- Asks the model for a minimal SEARCH/REPLACE patch.
- Applies the patch deterministically.
- Runs
pytestagain. - Retries under cost, wall-clock, stuck, and circuit-breaker caps.
- Marks the run
needs_approvalonly after RED->GREEN proof. - Invokes a Lemma Agent reviewer for an independent second opinion.
- Pauses at a human approval form.
- Opens a PR only after approval.
- Builds the PR body from the audit log.
- Supports replay from recorded
fix_events.
The current live path is deliberately tight. It is not an open-ended shell agent and not a multi-language repair platform. It is a trust-preserving Python/pytest repair workflow with one visible, inspectable spine.
flowchart TB
classDef app fill:#f8fafc,stroke:#111827,color:#111827
classDef lemma fill:#ffffff,stroke:#111827,color:#111827
classDef data fill:#f3f4f6,stroke:#374151,color:#111827
classDef ext fill:#ecfdf5,stroke:#166534,color:#052e16
classDef risk fill:#fef2f2,stroke:#991b1b,color:#450a0a
User["Maintainer"] --> App["grappy-app<br/>Vite + React Lemma App"]
App --> Workflow["change_run Workflow<br/>FORM -> fix_loop -> reviewer -> approval -> open_pr"]
subgraph Pod["grappy-pod on Lemma"]
Workflow --> FixLoop["fix_loop Function<br/>reproduce -> plan -> patch -> test -> guard"]
Workflow --> Reviewer["reviewer Lemma Agent<br/>second opinion"]
Workflow --> Approval["Human approval FORM<br/>approve or reject"]
Workflow --> OpenPR["open_pr Function<br/>PR-as-evidence"]
Workflow --> Reject["record_rejection Function"]
FixLoop --> Tables[(Lemma Tables)]
Reviewer --> Tables
OpenPR --> Tables
Reject --> Tables
FixLoop --> Files[(Lemma Files)]
OpenPR --> Files
FixLoop --> LLM["Azure OpenAI<br/>coder + triage deployments"]
FixLoop --> Pytest["Lemma-native pytest sandbox<br/>real exit codes"]
end
Tables --> Replay["replay Function<br/>ordered event trajectory"]
OpenPR --> GitHub["GitHub App<br/>branch + pull request"]
Index["index_repo Function<br/>AST repo map + symbols"] --> Tables
Index --> Files
GitHub --> Index
class App app
class Workflow,FixLoop,Reviewer,Approval,OpenPR,Reject,Replay,Index lemma
class Tables,Files data
class GitHub,LLM ext
class Pytest risk
Every major product capability collapses onto two data structures:
change_runs: the state of one governed repair.fix_events: the append-only evidence log for that repair.
That is the product architecture in one sentence. The app, workflow, functions, agent, approval form, replay function, and PR body all read from or write to this spine.
stateDiagram-v2
[*] --> queued
queued --> reproducing
reproducing --> unreproducible: unpatched pytest exits 0
reproducing --> localizing: unpatched pytest exits non-zero
localizing --> planning
planning --> fixing
fixing --> fixing: failed patch or failing tests
fixing --> escalated: max iters, cost cap, wallclock, stuck, circuit breaker
fixing --> reviewing: RED->GREEN
reviewing --> needs_approval: reviewer verdict recorded
needs_approval --> rejected: human rejects
needs_approval --> pr_opened: human approves
unreproducible --> [*]
escalated --> [*]
rejected --> [*]
pr_opened --> [*]
sequenceDiagram
participant M as Maintainer
participant A as Lemma App
participant W as change_run Workflow
participant F as fix_loop Function
participant T as Lemma Tables
participant R as reviewer Agent
participant P as open_pr Function
participant G as GitHub
M->>A: Pick repo, describe bug, provide failing pytest target
A->>W: Start manual workflow
W->>F: Run caged fix loop
F->>T: create change_run + run_created
F->>F: run pytest on unpatched code
F->>T: repro_attempted + repro_confirmed
F->>T: localization_ranked + plan_drafted
loop bounded attempts
F->>F: ask model for minimal patch
F->>F: apply SEARCH/REPLACE patch
F->>F: run pytest
F->>T: patch_proposed + test_run
end
F->>T: regression_passed and status needs_approval
W->>R: Review change_run evidence
R->>T: append review_verdict
W->>M: Pause on approval form
M->>W: Approve or reject
W->>P: If approved, open evidence PR
P->>G: Create branch and PR via Git Data API
P->>T: append pr_opened
A successful run reads like a lab notebook:
run_created
repro_attempted
repro_confirmed
localization_ranked
plan_drafted
patch_proposed
test_run
regression_failed
reflexion_note
patch_proposed
test_run
regression_passed
review_verdict
pr_opened
The exact event types are declared in
grappy-pod/tables/fix_events/fix_events.json.
On the approve path, the approver is recorded in the pr_opened payload. On the reject
path, record_rejection writes an approval_decision event.
| Lemma primitive | Where it lives | What it does |
|---|---|---|
| Pod | grappy-pod/pod.json |
Owns the backend, data, functions, workflow, agent, files, and permissions. |
| Tables | grappy-pod/tables/ |
Store run state, audit events, repo metadata, GitHub installations, symbols, and issue analysis. |
| Files | /repomaps, /patches, /prs, /secrets in pod storage |
Hold repo maps, winning patches, generated PR bodies, and private credentials. |
| Functions | grappy-pod/functions/ |
Run deterministic work: fix loop, indexing, sandboxing, LLM calls, PR creation, replay, GitHub sync. |
| Workflow | grappy-pod/workflows/change_run/change_run.json |
Durable macro state machine with intake, repair, review, approval, rejection, and PR handoff. |
| Agent | grappy-pod/agents/reviewer/ |
Independent reviewer that reads evidence and appends review_verdict. |
| App | grappy-app/ |
Maintainer control surface deployed as a Lemma App. |
| Permissions | Function and agent manifests | Grant least-privilege access to tables and folders. |
This is not a thin wrapper around Lemma. Lemma is the coordination layer, the data layer, the approval layer, and the app host.
Gappy/
README.md # this file
AGENTS.md # project instructions for Codex agents
grappy-pod/ # Lemma pod: backend, data, workflow, agent
pod.json
agents/
reviewer/
instruction.md
reviewer.json
workflows/
change_run/
change_run.json
tables/
change_runs/
fix_events/
github_installations/
issue_analyses/
repo_symbols/
repos/
functions/
fix_loop/
gh_sync/
gh_token/
index_repo/
llm_complete/
open_pr/
record_rejection/
replay/
run_in_sandbox/
seed_demo_run/
executor/
grappy_executor/ # unit-tested local source of truth
tests/
pyproject.toml
eval/
autoloop_smoke.py # live smoke: RED -> model patch -> GREEN
seed_repo/
grappy-app/ # Vite/React Lemma App
lemma.app.json
package.json
src/
components/
lib/
pages/
routes.tsx
lemma-client.ts
styles.css
design-docs/ # product, architecture, execution, UI, reliability specs
knowledge-base/ # hackathon + Lemma vectorless RAG corpus
docs/
fixforge/
manifest.json
The live product is:
- backend:
grappy-pod/ - frontend:
grappy-app/
The design docs record earlier alternatives and cuts. The current direction is the all-Lemma path: Lemma Tables/Files for data, Lemma pod permissions for auth, Lemma Functions for backend actions, and a static Vite SPA deployed as a Lemma App.
The pod description is stored in grappy-pod/pod.json:
{
"name": "grappy",
"description": "Grappy - a supervised, audited, human-gated autonomous bug-fix engineer..."
}The pod owns:
- the repair workflow
- all durable data
- the append-only event log
- the code execution path
- Azure OpenAI calls
- GitHub App token minting
- repo indexing
- real PR creation
- rejection recording
- replay
- the reviewer Agent
The frontend is intentionally thin. It reads from Lemma Tables, starts workflow/function runs, submits approval decisions, and renders evidence.
The app is a Vite/React SPA deployed to lemma.work as a Lemma App. Its runtime
configuration is in grappy-app/lemma.app.json and
grappy-app/.env.example.
| Route | Purpose |
|---|---|
/ |
Activity list of all recent change runs |
/chat |
Start a new fix run |
/chat/:runId |
Inspect a live or historical run |
/settings |
Account settings |
/settings/github |
GitHub App connection and token verification |
/settings/repos |
Connected repositories and indexing controls |
Routes are declared in grappy-app/src/routes.tsx.
| Surface | Main files | What it shows |
|---|---|---|
| Activity | HomePage.tsx |
Minimal list of runs with status glyphs and timestamps. |
| Composer | Composer.tsx |
Repo picker, bug description, failing test, target file, guardrails. |
| Run View | RunView.tsx |
Live timeline, exit code proof, event details, diff, approval gate, evidence footer. |
| Settings | SettingsPage.tsx |
Account, GitHub connection, repos and indexing. |
The UI follows the project direction in
design-docs/11-frontend-dashboard.md:
- black and white, Linear-style
- color used only for semantic proof states: RED failure and GREEN success
- shadcn/Radix primitives and lucide icons
- Framer Motion via
motion/react - DiceBear-style identity direction in docs
- no marketing landing page; the first screen is the work surface
The app uses lemma-sdk@0.5.2 and lemma-sdk/react.
flowchart LR
App["React components"] --> Hooks["src/lib/hooks.ts"]
Hooks --> SDK["lemma-sdk/react"]
SDK --> Tables["Lemma Datastore queries"]
App --> Workflow["useWorkflow / useWorkflowForm"]
Workflow --> Lemma["Lemma workflow run + approval resume"]
App --> Functions["useFunctionRun"]
Functions --> Index["index_repo / gh_sync / gh_token / replay"]
Core hooks live in grappy-app/src/lib/hooks.ts:
useRunsuseRunuseRunEventsuseReposuseInstallationsuseLivePoll
erDiagram
REPOS ||--o{ CHANGE_RUNS : "selected for"
REPOS ||--o{ REPO_SYMBOLS : "indexed into"
CHANGE_RUNS ||--o{ FIX_EVENTS : "emits"
CHANGE_RUNS ||--o{ ISSUE_ANALYSES : "summarizes"
GITHUB_INSTALLATIONS ||--o{ REPOS : "grants access to"
REPOS {
uuid id
text github_full_name
text default_branch
enum index_status
file_path repomap_path
file_path conventions_path
text indexed_sha
datetime indexed_at
text installation_id
int symbol_count
}
CHANGE_RUNS {
uuid id
uuid repo_id
enum status
text issue_title
text issue_body
text fail_to_pass_test
text buggy_sha
float cost_spent_usd
float max_cost_usd
int iters
text pr_url
text repo_full_name
text langsmith_trace_url
}
FIX_EVENTS {
uuid id
uuid change_run_id
int seq
enum kind
json payload
file_path artifact_path
text langsmith_run_id
text idempotency_key
text actor
}
REPO_SYMBOLS {
uuid id
uuid repo_id
text symbol
enum kind
text file_path
int start_line
int end_line
text signature
float pagerank
}
| Table | File | Purpose |
|---|---|---|
change_runs |
change_runs.json |
One row per governed repair run. |
fix_events |
fix_events.json |
Ordered append-only event log and replay source. |
repos |
repos.json |
Connected repositories and index status. |
repo_symbols |
repo_symbols.json |
AST-extracted functions, classes, methods, and line ranges. |
github_installations |
github_installations.json |
GitHub App installation metadata. |
issue_analyses |
issue_analyses.json |
Phase-0 analysis twin for reproduction and plan metadata. |
change_runs.status is the maintainer-readable state:
queued
reproducing
unreproducible
localizing
planning
fixing
reviewing
needs_approval
approved
rejected
pr_opened
escalated
failed
Terminal states are honest. If the bug cannot be reproduced, Grappy says so. If the loop cannot converge within caps, it escalates instead of faking success.
| Function | Type | Purpose |
|---|---|---|
fix_loop |
JOB | Main caged loop: create run, reproduce, localize, plan, propose patch, test, guard, and move to needs_approval or escalated. |
run_in_sandbox |
JOB | Keystone executor for the seed demo: materializes workspace, applies optional patch, runs pytest, returns real exit code. |
llm_complete |
API | Hardened Azure OpenAI call site for smoke/eval flows. |
index_repo |
JOB | GitHub App tarball clone, Python AST parse, repo_symbols rows, repo-map file, repos.index_status='indexed'. |
gh_sync |
JOB | Poll GitHub App installations and accessible repos, then upsert installation and repo rows. |
gh_token |
API | Mint a 1-hour GitHub App installation token and return masked metadata for verification. |
open_pr |
JOB | Render PR body from fix_events, apply winning patch on a branch, open real GitHub PR if possible, else write evidence file. |
record_rejection |
JOB | Mark a run rejected and append the human decision. |
replay |
API | Read-only replay from the ordered event log. |
seed_demo_run |
JOB | Idempotently insert a completed demo run for a populated judge/operator dashboard. |
grappy-pod/functions/fix_loop/code.py is the
keystone implementation. It is self-contained because Lemma's hosted runtime loads each
function file independently.
The live loop:
- sources workspace files from a GitHub App tarball, inline files, or the embedded seed repo
- confirms RED with
pytest - writes
run_created,repro_attempted, andrepro_confirmed - loads repo-map and symbols when available
- writes
localization_ranked - drafts a plan with the triage model
- asks the coder model for minimal SEARCH/REPLACE blocks
- applies those blocks deterministically
- reruns
pytest - writes
test_run,regression_failed,reflexion_note, orregression_passed - stops on cost, iteration, wall-clock, stuck, or circuit-breaker caps
- moves the run to
needs_approvalonly after convergence
The current live function uses Aider-style SEARCH/REPLACE blocks:
checkout/pricing.py
<<<<<<< SEARCH
return order_total > FREE_SHIPPING_THRESHOLD
=======
return order_total >= FREE_SHIPPING_THRESHOLD
>>>>>>> REPLACE
The model proposes text. Grappy's code parses and applies it. The model does not get to mutate files directly.
The macro workflow lives in
grappy-pod/workflows/change_run/change_run.json.
flowchart TB
Intake["FORM: Describe the bug"] --> Run["FUNCTION: fix_loop"]
Run --> Converged{"DECISION: converged?"}
Converged -->|yes| Review["AGENT: reviewer"]
Converged -->|no| Escalated["END: escalated"]
Review --> Approval["FORM: human approval"]
Approval --> Decide{"DECISION: approved?"}
Decide -->|approve| PR["FUNCTION: open_pr"]
Decide -->|reject| Reject["FUNCTION: record_rejection"]
PR --> PREnd["END: PR opened"]
Reject --> RejectEnd["END: rejected"]
The reviewer Agent is declared in
grappy-pod/agents/reviewer/reviewer.json
and instructed in
grappy-pod/agents/reviewer/instruction.md.
It reads:
- the
change_runsrow - the ordered
fix_events - the
repro_confirmedevent - the final
regression_passed.winning_patch
It appends exactly one review_verdict event:
{
"verdict": "pass",
"rationale": "Minimal, test-backed change."
}The reviewer does not edit code, open PRs, or change the run status. It gives a second opinion before the human approval gate.
Grappy treats pytest exit codes as data, not exceptions:
- non-zero before patching means the bug is real enough to attempt
- zero after patching means the failing test is satisfied
- non-zero after patching sends the loop back with real output
- timeout returns
124
The executor contract is documented in
design-docs/09-execution-sandbox.md and mirrored
in the tested executor package.
| Risk | Defense |
|---|---|
| Agent claims success without running tests | Only pytest exit code 0 can converge. |
| Patch accidentally depends on dirty state | The loop materializes a fresh workspace for test runs. |
| Patch does not apply | Parser rejects it and records a reflexion_note. |
| Model repeats itself | Stuck detector escalates repeated identical patches. |
| Cloud/model failure burns budget | Circuit breaker and retry caps stop the run. |
| Secrets leak into prompts or logs | Secret regex redaction and untrusted-data wrappers. |
| Prompt injection in repo or issue text | Injection patterns are flagged and repo content is wrapped as untrusted data. |
| Guard | Default / behavior |
|---|---|
max_iters |
4 by default in the workflow intake |
max_cost_usd |
1.0 by default |
max_wallclock_s |
600 by default |
| circuit breaker | 3 consecutive infra failures |
| stuck detector | repeated identical patch signatures |
| file cap | repo tarball files capped before loading into workspace |
| output cap | prompts wrap and truncate long untrusted blocks |
Grappy uses a GitHub App, not a broad OAuth token.
- per-repository installation scope
- short-lived installation tokens
- bot identity for PRs
- GitHub's own "all repos vs selected repos" screen acts as the repo picker
- server-side token minting lives inside Lemma Functions
| Function | What it does |
|---|---|
gh_sync |
Lists installations and accessible repositories, updates Lemma Tables. |
gh_token |
Mints a masked, smoke-testable installation token. |
index_repo |
Uses the token to download a tarball and index Python symbols. |
fix_loop |
Uses the token to source a real repo workspace. |
open_pr |
Uses the token to create blobs, tree, commit, branch, and pull request. |
open_pr uses the Git Data API:
base ref -> base commit -> base tree
winning patch -> updated blobs
updated blobs -> new tree
new tree -> commit
commit -> branch ref
branch ref -> pull request
If a real repo or patch cannot be recovered, it still writes the evidence body to
/prs/pr-<change_run_id>.md and records the fallback as lemma-file:<path>.
Grappy's observability is intentionally boring: one row after another, in order.
flowchart LR
FixLoop["fix_loop"] --> Events["fix_events rows"]
Reviewer["reviewer Agent"] --> Events
Approval["Human approval"] --> Events
OpenPR["open_pr"] --> Events
Events --> UI["Run timeline"]
Events --> Replay["replay API"]
Events --> PRBody["PR body"]
Events --> Audit["Audit query"]
fix_events.payload carries the step-specific proof:
exit_codepassed,failed,errors,skippedduration_msstdout_tail- test list summaries
- patch text
- deployment and token usage metadata
- reviewer verdict
- approver and PR metadata
grappy-pod/functions/replay/code.py reconstructs
the timeline from the recorded events. It does not rerun the model. It re-shows the
ordered trajectory, derived facts, exit codes, iterations, cost, verdict, approver, and PR.
That is an honest replay: it audits what happened rather than asking the agent to re-narrate it.
- Node.js compatible with Vite 7 and React 19
- npm
- Python 3.11+ for the executor package
uvfor executor tests- Lemma CLI for pod import, function runs, and app deploy
- GitHub App credentials in pod Files for real repository runs
- Azure OpenAI credentials in pod Files for model-backed repair
cd grappy-app
npm install
npm run devUseful scripts:
npm run dev
npm run build
npm run previewRuntime configuration is read from .env.local or .env.development.local. The
template is:
cp grappy-app/.env.example grappy-app/.env.localImportant values:
VITE_LEMMA_API_URL=https://api.lemma.work
VITE_LEMMA_AUTH_URL=https://lemma.work/auth
VITE_LEMMA_POD_ID=<pod uuid>
VITE_LEMMA_APP_BASE_PATH=/
The executor package is the tested local source of truth for patch parsing, pytest output parsing, guards, prompt helpers, repomap behavior, and safety helpers.
cd grappy-pod/executor
uv run pytest -qLEMMA_ORG_ID=<org id> lemma pods import grappy-pod --pod grappyRun the seed executor without a patch:
lemma functions run run_in_sandbox --pod grappy -d '{"patch":"","test_target":"tests","timeout":120}' --waitRun the main loop on the built-in seed demo:
lemma functions run fix_loop --pod grappy -d '{"max_iters":4,"max_cost_usd":1.0,"max_wallclock_s":600}' --waitRun a real repository bug when the GitHub App has access:
lemma functions run fix_loop --pod grappy -d '{
"repo":"theCodeForgerHQ/grappy-demo",
"ref":"main",
"issue_title":"Free shipping boundary off-by-one",
"issue_body":"Orders exactly at the free-shipping threshold are wrongly charged shipping.",
"fail_to_pass_test":"tests/test_pricing.py",
"target_path":"checkout/pricing.py",
"test_target":"tests/test_pricing.py",
"max_iters":4
}' --waitgrappy-pod/eval/autoloop_smoke.py drives:
run_in_sandboxunpatched -> REDllm_complete-> model patchrun_in_sandboxpatched -> GREEN
python grappy-pod/eval/autoloop_smoke.pyThis smoke uses live Lemma and Azure credentials. It is not a unit test.
LEMMA_ORG_ID=<org id> lemma pods import grappy-pod --pod grappycd grappy-app
npm run build
lemma apps deploy grappy-app . --pod grappy --dist-dir dist -yThese are read from pod Files, not committed:
| Pod file | Used by |
|---|---|
/secrets/azure_openai.json |
fix_loop, llm_complete |
/secrets/langsmith.json |
best-effort trace link in fix_loop |
/secrets/github_app.json |
GitHub App metadata and optional installation id |
/secrets/github_app_key.pem |
GitHub App JWT signing |
Expected Azure OpenAI secret shape:
{
"endpoint": "https://<resource>.openai.azure.com",
"api_key": "<key>",
"api_version": "<api version>",
"coder_deployment": "<coder deployment>",
"triage_deployment": "<triage deployment>",
"fallback_deployment": "<fallback deployment>"
}Expected GitHub App metadata shape:
{
"app_id": "<app id>",
"installation_id": "<optional installation id>"
}The strongest demo starts with proof, not a prompt.
-
Open with the maintainer pain. "AI agents can write patches. The scary part is trusting one in a real repo."
-
Pick an indexed repo. Show that the repository is connected through the GitHub App and indexed into
repo_symbols. -
Start one scoped run. Provide one bug, one failing pytest target, and one target file.
-
Show RED before code. The first visual proof is the unpatched
pytestresult with a non-zeroexit_code. -
Show localization and plan. The run records why it believes the suspect file/symbol is relevant.
-
Show the loop. A patch is proposed, applied, tested, and either retried or converged from real output.
-
Show GREEN. The exit code flips to
0; the final proof strip shows tests, time, cost, and iterations. -
Show the reviewer. The Lemma Agent verdict appears before approval.
-
Approve. The workflow resumes only after the human decision.
-
Open the PR. The PR body is generated from the audit log.
-
Replay. Show that the run can be inspected again from
fix_events, without asking the model.
The line to land:
Grappy does not ask you to trust an agent. It hands you the evidence a maintainer would need to decide.
| Cut | Why |
|---|---|
| Autonomous merge | The human approval gate is the product. |
| Multi-language repair | Python/pytest keeps the oracle and sandbox path concrete. |
| Whole-suite CI by default | Selected/scoped tests keep runs fast and cost-bounded. |
| Agent-generated acceptance criteria as the main path | The failing test must remain independent of the repair agent. |
| Broad multi-agent sprawl | One repair loop plus one reviewer is easier to audit than an agent zoo. |
| Separate database/auth stack | Lemma Tables, Files, and pod permissions are the intended platform fit. |
| SSR frontend | lemma.work serves the Vite SPA as the Lemma App path. |
| Area | Current live path | Future option |
|---|---|---|
| Patch generation | Single candidate per attempt, bounded retries | Best-of-N execution rerank when budget allows |
| Patch format | SEARCH/REPLACE blocks | Codex apply_patch envelope for models optimized for it |
| Repo indexing | Python AST symbols and markdown repo-map | richer graph/PageRank or optional semantic column |
| Replay | Recorded trajectory replay | deterministic re-execution where safe and cheap |
| CI | Sandbox pytest execution | cite GitHub Actions checks after PR opens |
| Languages | Python | additional language adapters only after Python path is reliable |
The next work should not widen the product until the core loop is undeniable.
- Harden real-repo test scoping so every run's
test_targetmatches the single-bug scope. - Add richer regression selection from the
repo_symbolsgraph. - Persist raw stdout/stderr as Files for every test event, not only tails.
- Add explicit
approval_decisionrendering in the UI timeline. - Read GitHub Actions status on opened PRs and cite it in the evidence footer.
- Add optional best-of-N candidate sampling behind a cost-aware toggle.
- Expand from Python-only only after the maintainer trust loop remains stable.
This repository includes a vectorless RAG corpus in knowledge-base/.
When answering hackathon, Lemma SDK, judging, timeline, submission, or strategy questions,
use the manifest first:
knowledge-base/manifest.json
knowledge-base/INDEX.md
knowledge-base/TAGS.md
knowledge-base/retrieval-guide.md
Key nodes used to frame this README:
kb-judging: rubric weights and evaluation focuskb-winning-strategy: one sharp user, one core loop, defend scopekb-lemma-concepts: Pods, Tables, Files, Agents, Workflows, Functions, Approvals, Appskb-submission: live Lemma App/pod, demo, and writeup expectations
The project-specific design corpus lives in knowledge-base/fixforge/.
Grappy is not trying to be the fastest patch writer. It is trying to be the repair workflow a maintainer can trust:
known failing pytest
-> reproduce RED
-> localize and plan
-> patch under guardrails
-> verify GREEN
-> reviewer second opinion
-> human approval
-> PR-as-evidence
-> replayable audit trail
One repo. One bug. One human gate. Real exit codes. Evidence before trust.