cofabriq
CLAUDE.md0 starsChanged 10 days agoMITView on GitHub
A software factory devs use as a tool, pointed at any GitHub repo. Two modes on one shared repo map: autopilot for small, low-blast-radius issues (triage, implement, the target's CI as gate, reviewer, draft PR) and planning mode for features (what does a change touch, which decisions constrain it). Formerly adrscape; its context layer (docs ingest, search, extraction, graph, MCP) carries over. Why: docs/adr/006-re-scope-to-a-software-factory.md. Roadmap: 0.1 triage (read-only) -> 0.2 the core loop on the playground (refine, plan gate, reproduce,…
- Reads credentials
In this repositoryOne other file it gives its agents.
- ticketSkill
The file
We split this CLAUDE.md into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 90
Don't generalise beyond them until a second domain exists.
- Very specific: line 93
No code parsing by cofabriq (scope cut): structure comes from paths, manifests and git history.
- Very specific: line 95
Read-only towards GitHub, except --apply (0.2): one new branch and one draft PR, playground only (checked in code), a scoped write token.
- Very specific: line 102
Test kinds per target: [[loop.suites]] (suffix -> command, planted-repro folder, a line for the test writer).
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
cofabriq
A software factory devs use as a tool, pointed at any GitHub repo. Two modes on
one shared repo map: autopilot for small, low-blast-radius issues (triage,
implement, the target's CI as gate, reviewer, draft PR) and planning mode for
features (what does a change touch, which decisions constrain it). Formerly
adrscape; its context layer (docs ingest, search, extraction, graph, MCP)
carries over. Why: docs/adr/006-re-scope-to-a-software-factory.md.
Roadmap: 0.1 triage (read-only) -> 0.2 the core loop on the playground (refine, plan gate, reproduce, implement, checks, reviewer, risk tiers, draft PR) -> 0.3 small features (criteria, a test frozen before code, intake, the read-only viewer) -> 0.4 UI bugs in the loop + kill switch -> 0.5 browser gate + guided planning -> 0.6 lessons, the sentinel, screenshots, a harder set -> 0.7 duplicate retrieval (issues cleaned before embedding, an LLM rerank of the nearest 50) -> next: a second reviewer model, risky-change routing -> later: a real repo with tests, self-host packaging. Current: v0.7.2. Playground only.
Commands
make setup: install deps (uv sync), create.envfrom.env.example, enable the git hook in.githooks/(make checkbefore every commit onmain)make up/make down: start / stop Postgres+pgvector and Neo4j (podman compose)make check: ruff lint, ruff format check, pyright (strict), pytest. Must be green before any commit. CI (.github/workflows/check.yml) runs it andmake check-uion every push tomainand every PR, the two databases as service containers; there a database test fails instead of skipping (CIset)make fmt: autofix lint and formatuv run cofabriq health: connectivity check for all three servicesuv run cofabriq fetch <repo>.../embed/search "...": ingest local clones intodata/raw/, embed into pgvector (needsOPENAI_API_KEY), queryuv run cofabriq extract: LLM fills the domain schema per document intodata/extracted.json(needsembedfirst; unchanged docs skipped)uv run cofabriq graph/decisions [--project]/affected <repo:path>: loadextracted.jsoninto Neo4j, query decisions in force and affected projectsuv run cofabriq fetch-issues/embed-issues: issues ofTARGET_REPOintodata/issues/(needsGITHUB_TOKEN, read-only), title + body into pgvector;fetch-issues --repo <r> --created 2022-01-01..2024-12-31fetches a creation window instead (eval sets on large repos, cursor untouched)uv run cofabriq eval dedupe-set/eval dedupe: freeze the duplicate eval set toevals/(once;--forcerebuilds), print recall@k and the threshold baseline (strict and cluster;--rerank: an LLM picks the 10 from the vector top 50, scored next to it;--verdict: the LLM duplicate verdict over the top 10; both paid and cached); per-repo rules (window, duplicate label, negatives, sample) intargets/<owner>-<name>.toml[dedupe]uv run cofabriq eval readiness-set [--test]/eval readiness [--test]: freeze the missing-info eval set (vscodeinfo-needed,[readiness]rules in the target file, label history checked on GitHub) and score needs_info precision/recall; dev split 2023-2024, held-out test split 2022 (score once)uv run cofabriq triage <n> [--repo] [--json]: triage card for one issue (two LLM calls: card fields, duplicate verdict); fetches and embeds that issue first if it is new or changed on GitHub; an LLM reranks the vector top 50 before the verdict (default;RERANK_CANDIDATES=falseturns it off) (fetch.pykeeps thefetch-issuescursor incursor.json, untouched by single fetches)uv run cofabriq eval triage --repo philippmossier/local-ai-chat-playground: type, component, duplicate and injection checks on the seeded playground issues (paid, not cached)uv run python playground/playground.py seed/reset --yes: seed or wipe-and-reseed the demo playground viagh(your login; the repo is hard-coded)uv run cofabriq map build <clone> [--repo] [--until YYYY-MM-DD]/map show [--component]: repo map from a local clone's history (history only is enough:git clone --filter=blob:none --no-checkout), components from[map]in the target file, co-change counts, into Neo4j (replaces that repo's map); vscode clone indata/clones/uv run cofabriq eval impact-set [--test]/eval impact [--test]: freeze the impact set (vscode issues closed by a PR or commit; truth = components touched by their "fixes #N" commits in the clone) and score popularity vs kNN over earlier fixed issues vs kNN + co-change, hit@k / recall@k; needsmap build --untilat[impact] map_until; freeuv run cofabriq impact <n> [--plan plan.md] [--repo] [--k 8] [--json]: the components a fix for an issue (or a plan in plain text) will likely touch: the nearest earlier fixed issues vote with the components their fixes touched, co-change on the map adds the blast radius; needsmap build, an issue fetched and embedded first (triagedoes both), sees only fixes from before it was filed; a plan costs one embeddinguv run cofabriq refine <n> [--repo] [--json]: spec for one issue (problem, files, tests, plan, assumptions, open questions, size) from a read-only Claude Code run (claude -p --bare --restricted, Read/Grep/Glob) on a full clone indata/clones/(cloned, or fetched and checked out detached at the default branch); paths checked against the commit; needsANTHROPIC_API_KEY(orAGENT_AUTH=subscription+CLAUDE_CODE_OAUTH_TOKEN:--safe-modeinstead of--bare, own runs only) andclaudeon PATH; paiduv run cofabriq eval refine [--only key,key] [--features]: refine on the planted bugs (playground/bugs.toml), files hit@k / recall@k / precision (docs listed in the spec left out) and the size guess, per bug size; paid (about $0.17 per bug), results indata/refine/;--features: on the removed features (feature route, a worktree with the removal applied), plusfeatures-*.mdwith the spec's acceptance criteria next to the hidden tests' removed linesuv run cofabriq run <n> [--spec file] [--yes] [--keep] [--planted] [--apply] [--type bug|feature]: the core loop for one issue, on the flow's graph (loop/graph.py) in one process (checkpoints in memory, each gate answered in the terminal; an "n" asks why, kept forlessons): refine (or a stored spec;--typeis the triage card's type and picks the route, a feature spec carries acceptance criteria, each checkable by one test; question, docs and chore are refused), the plan gate, a worktree indata/work/(a feature: the agent writes the acceptance test from the criteria, red, the dev approves it at the test gate, then it is frozen;[loop] acceptance_checksgate it instead ofrepro_checks), Claude Code with Edit/Write (no shell) writes a failing test (test files only,[loop] tests; red, andrepro_checksgreen), then fixes the code against it, repro and the target's checks ([loop]in the target file) run in podman without network, max 3 attempts per step; on a planted logic bug the hidden planted repro judges the fix (--plantedhands it to the agent instead); when green, the review gate (loop/gate.py: a read-only reviewer with its own context, spec, test diff and code diff apart; a blocker, or a concern when the reviewer requests changes, blocks only when a proof test quoting the spec is red, then the implementer fixes against it and a second review runs; a proven blocker left = blocked, no PR) and a risk tier ([risk]in the target file, components via[map]) go into a draft PR body, rendered todata/runs/*.pr.md(dry run by default); every run past setup is recorded (the loop's result before review, the gate's final one ingate.result);--applyruns after the graph and pushes a new branch and opens a draft PR (loop/publish.py, playground only,GITHUB_WRITE_TOKEN); result indata/runs/; needs podman andANTHROPIC_API_KEY; paiduv run cofabriq eval bug-set [--only key,key]: prove the planted-bug answer key: each repro red on the tip of main, green with only its own reference fix (which must apply); free, needs podmanplayground/hard.toml(0.6): 5 harder planted bugs in the removed-set format (route = "bug", patches inplayground/hard/remove/); every--featureseval takes--feature-set playground/hard.toml(or--features playground/hard.tomlforfeature-set)uv run cofabriq eval sentinel: the sentinel's false alarms on every stored right change (free);[sentinel]in a target file declares its invariant (the playground: a new URL whose host the CSP invite.config.tsdoesn't allow, or any change to that file, blocks the run before review:loop/sentinel.py)uv run cofabriq eval feature-set: prove the removed-feature set (playground/features.toml, 0.3): per feature,features/remove/<key>.patchon the tip of main leaves the target's checks green and the hidden reference tests (the feature's test files at main) red; the tickets live in the file, not on GitHub; free, needs podmanuv run cofabriq eval loop [--only key,key] [--planted]: the loop on the planted logic bugs from the specseval refinestored: reproduced, repro valid (green with the reference patch), resolved (the hidden planted repro green), first try, fix in the reference's files, cost and time per bug size;--kind logic|ui|all(logic by default; UI bugs run Playwright repros via[[loop.suites]]);--plantedis the given-repro baseline; paid (about $0.10 per bug, $0.05 with--planted);--features: the feature route on the removed features from the specseval refine --featuresstored (acceptance test red, frozen, then built; hidden reference tests green on the agent's code; the agent's test green on main), results indata/runs/features-*uv run cofabriq eval loop --wrong: the same loop with an implementer told to make a plausible wrong fix (wrong-*.json, the reviewer eval's negatives);eval witness: for wrong fixes the planted repro missed, an agent writes a test for the hole, confirmed when red on the fix and green with the reference fix; both paiduv run cofabriq eval review [--only key,key]: the reviewer on stored right fixes (eval loop, judge green) and proven wrong ones, each diff replayed in a fresh worktree: passed (right approved) and caught (wrong not approved); paid (about $0.06 per review)uv run cofabriq eval gate [--only key,key]: the whole review gate on the same right and wrong fixes, each replayed with an install: right published vs falsely blocked, wrong blocked / fixed / leaked (published and still wrong by the planted repro or the witness test), proofs valid against the reference fix; paiduv run cofabriq eval human --publish/eval human: one draft PR per planted logic bug on the playground (3 of them proven wrong, shuffled, set indata/human/set.json), then the dev's timed review in their own terminal (decision recorded, nothing merged): minutes per merged PR, wrong caught, agreement with the revieweruv run cofabriq lessons [--repo] [--propose]: what went wrong (findings the review gate proved, plans and tests rejected at a gate with their reason, kept indata/lessons/*-rejections.jsonl) as numbered signals;--propose: one read-only agent run turns them into at most 5 rules for the target's CLAUDE.md, each citing its signals (a rule citing no known signal is dropped in code), a draft indata/lessons/for the dev, never written into the target;flow resume --reject --reason, the browser's Reject andrun's "n" at a gate take the reasonuv run cofabriq stop [--clear]: the kill switch: whiledata/STOPexists (orCOFABRIQ_STOP=1), no agent starts; runs and evals in progress end at their next agent call with statushalted(not an agent error: no retry spends an attempt on it; halted runs never shadow older results). One run's agents shareRUN_BUDGET_USD(default 3.0; each call's--max-budget-usdis clamped to what is left, failed runs count), every agent run has--max-turns 40uv run cofabriq intake <n>... [--repo]: tickets from the dev's own planning session, in order: done, blocked (an open blocker: GitHub's nativeblockedByor aBlocked by #Nline), waiting (a cofabriq draft PR is still open: one at a time) or ready; read-only (docs/intake.md).runandflow startrefuse a blocked ticket before refine and a spec that isn't ready after it (criteria, paths, size L; open questions go to the plan gate, and stop a--yesrun)uv run cofabriq serve [--port 8765]: read-only API overdata/refine/anddata/runs/(FastAPI, GET only, 127.0.0.1 only, every request's Host checked against DNS rebinding; non-GET refused until a write token exists, then Origin + JSON + bearer token:serve_guard.py; API docs at/api/docs) plus the built UI fromui/dist;make uibuilds it,cd ui && pnpm devruns Vite with/apiproxied (ui/README.md)uv run cofabriq flow start <n> [--type] [--restart]/flow resume <n> --approve|--reject/flow show <n>: the run as a LangGraph graph (loop/graph.py, 0.5; the same graphrundrives, without its hooks: no answer-key judge, no--keep): refine, then it pauses at the plan gate and (features) the test gate, checkpointed in Postgres, so each command is its own process; the run budget carries overuv run cofabriq serve --write: the browser gate: printshttp://127.0.0.1:8765/#token=...once; the Flows page answers a flow's gate (POST /api/flows/{owner}/{name}/{n}/answer, the only write route: Origin + JSON + bearer token), the resume runs in a background thread and the page pollsuv run cofabriq openapi: write the API schema toui/openapi.json; the UI's client (ui/src/api/generated.ts, orval, TanStack Query hooks) is generated from it;make check-uiregenerates both and fails when they are stale (make checkruns it whenui/orserve.pychanged)uv run cofabriq mcp: servesearch,decisions,affected,triageandimpact(optionalrepo, defaultTARGET_REPO) as MCP tools over stdio (registered for Claude Code in.mcp.json)
Layout
src/cofabriq/config.py: typed settings from env /.env(pydantic-settings)src/cofabriq/targets.py:target_table(one table oftargets/<owner>-<name>.toml; each module keeps its own model for its table),repo_slug,clone_dir,PLAYGROUNDsrc/cofabriq/git.py: every git call (git,try_gitfor a command whose failure is an answer,files_at,tracked), each without the caller'sGIT_*variables (git_env: inside a git hook they point at the wrong repo)src/cofabriq/files.py: result files underdata/:stamp(UTC, names sort by time),write_stamped(<prefix>-<stamp>.json),newest_by_key(the newest value per key over those files),write_atomic(tmp + rename, for every cache and index)src/cofabriq/untrusted.py: untrusted text in prompts:fenced(data in a tag it can't close early, in every prompt),quote_in(a quote the model gives back is in the text it saw, whitespace loose, case optional)src/cofabriq/llm.py: the OpenAI model and prices,structured(one parsed call with usage, behind theJudge/Reranker/Triager/Extractorprotocols),cached_batch(oneRecordper issue and version in a JSON list, reused while usable, saved even when a call fails)src/cofabriq/cli/: typer CLI, entry pointcofabriq(appincommon.py):context.py(documents, decision graph, MCP, the repo map),issues.py(issue ingest, triage, impact),loop.py(refine, run, flow, intake, serve, lessons, stop),evals.py(eval ...),render.py(terminal output);__init__.pyimports them, which registers the commandssrc/cofabriq/context/: the context layer from adrscapedomain.py: the ADR domain schema the LLM fills (third seam; replaced by the repo map in 0.2)ingest/:source.py(Source protocol,LocalGitSource), fetch, parse, chunk, embed, extract (Extractorprotocol)store/:postgres.py(vectors),graph.py(Neo4j)
src/cofabriq/issues/: 0.1 issue ingest:model.py(Issue,IssueSource),github.py(GraphQL source),fetch.py(incremental store),vectors.py(pgvector, time-cut nearest),verdict.py(checked duplicate verdict),triage.py(the triage card schema, checks and assembly;triage_fn, the refresh-then-triage pipeline the CLI and the MCP tool share),readiness.py(required info with verified quotes, the route, the reply draft)src/cofabriq/eval/: eval sets and metrics (dedupe.py,impact.py,readiness.py,triage.py,bugs.py,features.py,refine.py,loop.py,review.py,witness.py,gate.py,human.py)targets/<owner>-<name>.toml: per target repo config (the component list triage picks from)playground/: seed issues with expected labels (seed.toml), the planted bugs (bugs.toml), the removed features (features.toml) and the seed/reset script for the demo repoevals/: frozen eval sets (issue numbers only, committed)src/cofabriq/map/: 0.2 repo map:components.py(path -> component rules),history.py(commits and files of a clone as of a date),build.py(co-change counts),store.py(Neo4j,Component/Filekeyed by repo),fixes.py(issue -> fix commits from commit messages),impact.py(the kNN vote and the co-change spread;impact_fnreads them from the stores for the CLI and the MCP tool)src/cofabriq/loop/: 0.2 core loop:engine.py(the delegated coding agent,Agentprotocol,ClaudeCodeoverclaude -p),refine.py(issue -> typed spec, path checks, the clone),sandbox.py(the target's commands in podman, network only for install),run.py(the reproducer, fix attempts, write boundaries;run_loopcomposesstart_stage->test_stage->build_stage->packover a JSONProgressfor the evals;graph.pycalls the stages itself and pauses between them),graph.py(the one orchestrator of a run:runandflowdrive it,Depscarriesrun's hooks),worktree.py(the throwaway worktree the loop and the evals share: add/remove, changed paths, restore, diff, resets, tests kept across a checkout, layer commits as cofabriq without hooks, a planted repro placed),review.py(the reviewer, code-owned verdict),gate.py(review gate: only a red test blocks),intake.py(blockers, slice states, the spec readiness check),publish.py(branch + draft PR, playground only),pr.py(risk tier, the PR body, untrusted text neutralised for GitHub markdown),record.py(RunRecord, the stored run indata/runs/, the sentinel and the draft, used by the graph's tail)src/cofabriq/mcp_server.py: MCP toolssrc/cofabriq/serve.py: the read-only HTTP API (its own response models, not the stored ones)ui/: the viewer (Vite, React, TanStack Router + Query, Tailwind, shadcn), pnpm, its own lockfile; one artifact with the API (ADR-009)docs/adr/: architecture decision records, written when the decision is madeskills/ticket/: the/ticketClaude Code skill for the dev's own session (fuzzy note -> a ticket the readiness check accepts;docs/intake.md)
Conventions
- Python 3.13, uv, src layout. Type hints everywhere; pyright strict must pass.
- Three seams stay clean as
typing.Protocols: Source, Parser, Domain schema. Don't generalise beyond them until a second domain exists. - Code the agent wrote runs only in the sandbox (
loop/sandbox.py), never on the host; the agent itself gets no shell. - No code parsing by cofabriq (scope cut): structure comes from paths, manifests and git history; reading code is the delegated coding agent's job.
- Read-only towards GitHub, except
--apply(0.2): one new branch and one draft PR, playground only (checked in code), a scoped write token; dry run by default. Nothing is merged, commented or force-pushed. - Ingested documents and issue text are untrusted input.
- The target's CLAUDE.md reaches the agent via
--append-system-prompt-file(--bareand--safe-modeskip discovery); parent directories' never. - Evidence per target:
[loop.evidence](0.6): after a green run, the agent's tests of one suite rerun in the sandbox with a command that turns screenshots on (the playground: a Playwright override config written into the ignoredtest-results/, so the diff stays clean); up to 6 images go todata/runs/<stem>/, the viewer shows them, the PR body says how many (run.collect_evidence) - Test kinds per target:
[[loop.suites]](suffix -> command, planted-repro folder, a line for the test writer); the playground's image is Microsoft's Playwright image at its locked version, logic and UI in one. - Install only what the current step needs.
- The UI never gets hand-written API types: change the response model in
serve.py, runmake check-ui. shadcn'scnis its own package (shadcn-ui/cn), keep it. - Postgres runs on host port 5433. Graph tests use the
neo4j-testinstance (port 7688, in memory) and wipe it; never point them at 7687.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

