Step 9 — autonomous patch loop:
- patcher.py: clawdforge session → unified diff → worktree apply → verify recipe → push branch → open Gitea PR
- migration 007: patch_attempts (UNIQUE per finding+attempt, max 3 attempts)
- runner.py: post-parse hook fires patcher.maybe_draft_for_job when notify.auto_patch=true
- server.py: POST /jobs/{id}/patches, GET /patches, GET /patches/{id}
- digest.py: patch-drafted lines + open-follow-up count via Gitea PR state check
- mcp: crafting_table_draft_patch stub replaced with real implementation
- tests/test_patcher.py + tests/test_patches_api.py: 27 new tests
No auto-merge — patches stop at PR-open. Cobb merges.
Step 10 — production recipes:
- examples/recipes/clawdforge.json: 14 subprojects across all SDKs, audit nightly
- examples/recipes/cauldron.json: single Flask subproject, audit nightly
- examples/recipes/tradecraft.json: nightly audit, auto_patch=false (manual review)
- examples/register-all.sh: bulk-register helper with GITEA_TOKEN substitution
- README "Autonomous patch loop" + "First production recipes" sections
Tests: server 116→143, mcp 65→67. All green.
Spec: memory/spec-crafting-table.md
41 lines
1.1 KiB
TOML
41 lines
1.1 KiB
TOML
[build-system]
|
|
requires = ["setuptools>=68", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "crafting-table"
|
|
version = "0.1.0"
|
|
description = "Polyglot dev/build/audit container — HTTP API + async job runner."
|
|
readme = "README.md"
|
|
license = { text = "MIT" }
|
|
requires-python = ">=3.11"
|
|
authors = [
|
|
{ name = "Kayos", email = "kayos@sulkta.com" },
|
|
]
|
|
dependencies = [
|
|
"fastapi>=0.115,<1.0",
|
|
"uvicorn[standard]>=0.30,<1.0",
|
|
"pydantic>=2.7,<3.0",
|
|
"httpx>=0.27,<1.0",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
test = [
|
|
"pytest>=8.0",
|
|
"pytest-asyncio>=0.23",
|
|
"httpx>=0.27",
|
|
]
|
|
|
|
[tool.setuptools.packages.find]
|
|
include = ["crafting_table*"]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
filterwarnings = [
|
|
# asyncio's BaseSubprocessTransport.__del__ can fire after the test loop
|
|
# closes when subprocess cleanup races with pytest-asyncio's loop teardown.
|
|
# The test logic verifies the subprocess actually died; the late __del__
|
|
# touch is harmless. Suppressing keeps pytest's AST cache from blowing up.
|
|
"ignore::pytest.PytestUnraisableExceptionWarning",
|
|
]
|