feat(projects): approval guard + worktree-aware ralph execution
Two structural fixes that together let users manage feature-branch
work without manual intervention:
Approval guard — `/plan/start` returns 409 `already_committed` if the
project status is approved/running/complete, unless the body opts in
with `force=true`. Frontend now renders "Re-planifică" instead of
"Planifică" on approved cards and gates it behind a confirm dialog
that threads `force=true` through. Prevents an accidental click from
wiping `status=approved` and burning a fresh planning subprocess.
Worktree awareness — projects can now declare that they target a
feature branch on an existing Gitea repo, not a repo-per-slug clone.
Three optional fields added to approved-tasks.json: `repo` (default
= slug), `branch` (feature branch to create), `base_branch` (default
main). Wired through `/p` flag parser in router.py, the dashboard
Propose modal's new "Avansat" section, and the night-execute prompt
which clones {repo} and creates {branch} from {base_branch} before
running ralph.
CLAUDE.md updated with both flows + the new schema fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -325,12 +325,40 @@ def _try_ralph_dispatch(text: str, adapter_name: str | None = None) -> str | Non
|
||||
return None
|
||||
|
||||
|
||||
def _parse_propose_flags(text: str) -> tuple[dict, str]:
|
||||
"""Strip leading --repo/--branch/--base-branch flags from text.
|
||||
|
||||
Returns (flags_dict, remaining_text). Flags are accepted in any order before
|
||||
the description. Unknown tokens are left in remaining_text.
|
||||
"""
|
||||
tokens = text.split()
|
||||
flags: dict[str, str] = {}
|
||||
consumed = 0
|
||||
while consumed < len(tokens):
|
||||
tok = tokens[consumed]
|
||||
if tok in ("--repo", "--branch", "--base-branch") and consumed + 1 < len(tokens):
|
||||
key = tok.lstrip("-").replace("-", "_")
|
||||
flags[key] = tokens[consumed + 1]
|
||||
consumed += 2
|
||||
else:
|
||||
break
|
||||
return flags, " ".join(tokens[consumed:]).strip()
|
||||
|
||||
|
||||
def _ralph_propose(slug: str, description: str) -> str:
|
||||
"""Adaugă un proiect cu status pending în approved-tasks.json.
|
||||
|
||||
Schema includes the W2 planning fields (`planning_session_id`,
|
||||
`final_plan_path`) so the orchestrator and PRD generator can find them.
|
||||
Description may be prefixed with optional flags:
|
||||
--repo <name> Gitea repo to clone (default: slug)
|
||||
--branch <name> Feature branch to create (default: none → main)
|
||||
--base-branch <name> Branch to fork from (default: main)
|
||||
|
||||
Example: /p roa2web-bonuri --repo roa2web --branch feature/bonuri "<descriere>"
|
||||
"""
|
||||
flags, description = _parse_propose_flags(description)
|
||||
if not description:
|
||||
return "Descriere lipsă după flag-uri. Folosire: /p <slug> [--repo X --branch Y] <descriere>"
|
||||
|
||||
data = _load_approved_tasks()
|
||||
|
||||
for p in data["projects"]:
|
||||
@@ -343,13 +371,22 @@ def _ralph_propose(slug: str, description: str) -> str:
|
||||
"status": "pending",
|
||||
"planning_session_id": None,
|
||||
"final_plan_path": None,
|
||||
"repo": flags.get("repo"),
|
||||
"branch": flags.get("branch"),
|
||||
"base_branch": flags.get("base_branch"),
|
||||
"proposed_at": datetime.now(timezone.utc).isoformat(),
|
||||
"approved_at": None,
|
||||
"started_at": None,
|
||||
"pid": None,
|
||||
})
|
||||
_save_approved_tasks(data)
|
||||
return f"📋 Adăugat: {slug}\n └ {description}\n\nAprobă cu: /a {slug}"
|
||||
|
||||
extras = []
|
||||
if flags.get("repo"): extras.append(f"repo={flags['repo']}")
|
||||
if flags.get("branch"): extras.append(f"branch={flags['branch']}")
|
||||
if flags.get("base_branch"): extras.append(f"base={flags['base_branch']}")
|
||||
extras_str = f"\n └ {' · '.join(extras)}" if extras else ""
|
||||
return f"📋 Adăugat: {slug}{extras_str}\n └ {description}\n\nAprobă cu: /a {slug}"
|
||||
|
||||
|
||||
def _ralph_approve(slugs: list[str]) -> str:
|
||||
|
||||
Reference in New Issue
Block a user