feat(forgejo): manually started implementer sessions #47

Merged
lucas merged 6 commits from 44-forgejo-manually-started-implementer-session into main 2026-10-20 13:31:01 +02:00
Collaborator

Start an unattended forgejo_implementer session for a Forgejo issue from the deck. "New session" in the sidebar and the session picker is now a split button. Its menu opens an "Implement issue" form with three fields: issue number, base branch and model. POST /api/forgejo/implement checks the issue and clones it into a per-issue checkout, detached at the base branch. It then starts the first turn under a write-capable policy with a 2-hour turn timeout. forgejo_sessions maps the issue to the session, and also the PR the session opens, so @mentions on either continue the session.

Decisions

  • Posted on #44:
    • startImplementer checks the issue's queue slot again after the async issue read.
    • Comment triggers on the issue wait in the queue until the first turn ends.
    • A missing base branch returns 422 with git's message.
    • The forced git-bash check is case-insensitive, like the permission-rule matcher.
    • The trigger-kind switches share one unknownKind helper.
  • Web UI:
    • NewSessionButton renders the modal itself.
    • The chevron is disabled under the same condition as the main button.
    • store.implementIssue repeats createSession's subscribe lines inline.
    • Enter submits the form, and server refusals show inline.

Verification

  • bun test src in apps/server: 831 pass, 0 fail. New tests:
    • policy: tool surface, raw-git deny, comment scope with PR mapping, timeouts
    • checkout: base branch, and a missing base branch
    • startImplementer (8 tests): creation, 409/404/422 refusals, queue blocking, reuse from the issue and the PR, 2-hour timeout report
  • bun test src in apps/web: 244 pass
  • tsc typecheck: clean for server and web
  • vite build: OK
  • Route smoke run with a throwaway script:
    • invalid JSON → 400
    • bad number → 400
    • bad model → 400
    • unknown workspace → 404
    • Forgejo not configured → 422
  • Not run: the manual end-to-end against a live Forgejo instance, and the UI in a browser.

Closes #44

Start an unattended `forgejo_implementer` session for a Forgejo issue from the deck. "New session" in the sidebar and the session picker is now a split button. Its menu opens an "Implement issue" form with three fields: issue number, base branch and model. `POST /api/forgejo/implement` checks the issue and clones it into a per-issue checkout, detached at the base branch. It then starts the first turn under a write-capable policy with a 2-hour turn timeout. `forgejo_sessions` maps the issue to the session, and also the PR the session opens, so @mentions on either continue the session. ## Decisions - [Posted on #44](https://git.sclu1034.dev/lucas/omp-deck/issues/44#issuecomment-16366): - `startImplementer` checks the issue's queue slot again after the async issue read. - Comment triggers on the issue wait in the queue until the first turn ends. - A missing base branch returns 422 with git's message. - The forced git-bash check is case-insensitive, like the permission-rule matcher. - The trigger-kind switches share one `unknownKind` helper. - [Web UI](https://git.sclu1034.dev/lucas/omp-deck/pulls/47#issuecomment-16371): - `NewSessionButton` renders the modal itself. - The chevron is disabled under the same condition as the main button. - `store.implementIssue` repeats `createSession`'s subscribe lines inline. - Enter submits the form, and server refusals show inline. ## Verification - `bun test src` in `apps/server`: 831 pass, 0 fail. New tests: - policy: tool surface, raw-git deny, comment scope with PR mapping, timeouts - checkout: base branch, and a missing base branch - `startImplementer` (8 tests): creation, 409/404/422 refusals, queue blocking, reuse from the issue and the PR, 2-hour timeout report - `bun test src` in `apps/web`: 244 pass - `tsc` typecheck: clean for server and web - `vite build`: OK - Route smoke run with a throwaway script: - invalid JSON → 400 - bad `number` → 400 - bad `model` → 400 - unknown workspace → 404 - Forgejo not configured → 422 - Not run: the manual end-to-end against a live Forgejo instance, and the UI in a browser. Closes #44
A write-capable unattended policy for sessions that implement a Forgejo
issue: code, git and Forgejo read tools plus forgejo_create_pr, with raw
git in bash always denied and comments limited to the issue and numbers
mapped to the session. Policies now carry their turn timeout (2 hours for
implementers, 30 minutes otherwise), and decide() receives the session id.

Co-Authored-By: Claude Opus 5.5 via omp-deck
Issue checkouts can be detached at origin/<baseBranch> instead of the
remote's default branch; implementer sessions restore theirs from the
base branch in their trigger scope.

Co-Authored-By: Claude Opus 5.5 via omp-deck
When forgejo_create_pr succeeds in an implementer session, the PR number
is mapped to the session in forgejo_sessions, so mentions on the PR
continue it and its policy allows commenting there. Done in the bridge so
it also covers prompts sent from the deck.

Co-Authored-By: Claude Opus 5.5 via omp-deck
ForgejoDispatcher.startImplementer checks the issue (exists, open, not a
PR, not already owned by a usable session or queued work), creates a
forgejo_implementer session in a per-issue clone, maps the issue to it,
and runs its first turn in the background while holding the issue's
queue slot. A failed turn is reported on the issue.

Turn timeouts now come from the session's policy, and a mapped
implementer session is reused for any number mapped to it, so mentions
on the PR it opened continue it.

Co-Authored-By: Claude Opus 5.5 via omp-deck
Starts an implementer session for an issue of the workspace's Forgejo
repository through ForgejoDispatcher.startImplementer, mapping its
refusals to HTTP statuses.

Co-Authored-By: Claude Opus 5.5 via omp-deck
"New session" in the sidebar and the session picker becomes a split
button whose menu opens an "Implement issue" form (issue number, base
branch, model) for the selected workspace. Submitting starts the
implementer session and opens it; server refusals show inline.

Co-Authored-By: Claude Opus 5.5 via omp-deck
Author
Collaborator

Web UI decisions:

  • NewSessionButton renders ImplementIssueModal itself, so the Sidebar and the session picker only swap their button for it. The chevron is disabled under the same condition as the main button (Global view, or no workspace path). It is not disabled while a plain session is being created.
  • store.implementIssue repeats createSession's five subscribe/activate/refresh lines inline. I left them unextracted rather than add a new store method for two callers.
  • Enter in any field submits the form, through a hidden submit button. Server refusals such as #N already has session … show inline in the form's error box.
Web UI decisions: - `NewSessionButton` renders `ImplementIssueModal` itself, so the Sidebar and the session picker only swap their button for it. The chevron is disabled under the same condition as the main button (Global view, or no workspace path). It is not disabled while a plain session is being created. - `store.implementIssue` repeats `createSession`'s five subscribe/activate/refresh lines inline. I left them unextracted rather than add a new store method for two callers. - Enter in any field submits the form, through a hidden submit button. Server refusals such as `#N already has session …` show inline in the form's error box.
lucas approved these changes 2026-10-20 13:30:55 +02:00
lucas merged commit db015c5ac6 into main 2026-10-20 13:31:01 +02:00
lucas deleted branch 44-forgejo-manually-started-implementer-session 2026-10-20 13:31:01 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
lucas/omp-deck!47
No description provided.