Codumentor logo Codumentor

SCM Push Plugin

The scm_push plugin gives the main agent a git_commit tool that creates real commits inside the credential-free sandbox. Those commits stay in the conversation's workspace overlay until a person clicks Push in the UI. The host-side push gateway then transports them using credentials the sandbox and the agent never see.

Requires Workspace Isolation and User Secrets.

Configuration

plugins:
  - module: codumentor.plugins.scm_push
    class: ScmPushPlugin
    priority: 25
    dependencies: ["workspace_isolation", "user_secrets"]
    args:
      enabled: true
OptionTypeDefaultDescription
enabledbooltrueEnable or disable the plugin.

Every git_commit message gets a Made with Codumentor trailer after a blank line.

What users do

  1. The agent commits with git_commit (stages all changes by default). Commits land in this session's overlay only — they are not pushed.
  2. The Push button in the chat header lights up.
  3. The user confirms the repo, branch, and SHA. Push runs on the host, never as an agent tool.

Bind a host-only push secret (HTTPS token or SSH key) in Settings → SCM Push, scoped to a repo (or glob), remote URL, and optional branch patterns. SSH bindings can also pin a host public key.

Until that binding exists — or while the secret it names is still empty — the dialog says so and its Push button stays disabled, so a missing credential is read before it is discovered. Readiness is answered by user_secrets' secret_store.has_host_secret, which reports only whether a value is set; the value itself is still read once, host-side, at push time.

Reviewing the changes

The dialog shows uncommitted work and pending commits for each repository. Review changes loads a per-file diff:

Uncommitted work is read from the conversation's own workspace, so it stays visible after a server restart or a session eviction: the dormant overlay is re-mounted read-only (writes discarded) to ask git what changed. When even that is impossible — no bwrap, or the workspace is gone — the dialog says the changes are unknown rather than reporting none, and sending any message reactivates the workspace.

When the target branch has moved on

Before the push is attempted, the dialog asks the remote what the target branch currently holds. If somebody else pushed to it while the session worked, it says so up front rather than letting a rejected push be the way you find out, and offers two ways forward:

The merge happens host-side in a throwaway git directory: nothing is fetched into the session's workspace, no credential ever enters the sandbox, and the result is an ordinary fast-forward — the gateway has no force of any kind and never rewrites what is already on the remote. The merge commit is the one commit Codumentor's server authors rather than transports; it is stamped with your commit identity and recorded in the push history under its own label ("Merged & pushed").

Merging is offered only while the conversation's workspace is readable. For a dormant conversation whose workspace has been swept, the branch-of-its-own remains available.

Commit identity (git_commit vs auto-commit)

git_commit stamps a real author. Resolution order:

  1. Settings → SCM Push — per-user name/email (the form pre-fills from the auth profile).
  2. Authenticated profile — display name and email, when available.
  3. The repository's own git config — user.name / user.email as git resolves them in that checkout (local → global → system), read host-side against the repo's real root. Used only when both are set; a half-configured repo is skipped rather than mixed with the fallback. This is the step that covers surfaces with no authenticated user — the TUI, unattended runs — where steps 1–2 cannot apply.
  4. Fallback — "Codumentor (identity unset)" <codumentor@localhost>, with a warning in the tool result. Commits are never blocked on a missing identity.

Steps 1–2 resolve once per turn (they key on the user); steps 3–4 resolve per commit (they key on the repository, which is only known from git_commit's repo argument). Step 3 is deliberately not a --local read: it answers "who would git commit in this checkout be authored as?", and most developers set their identity globally.

This is not workspace isolation's integration.auto_commit. Auto-commit runs after an isolated subagent finishes and uses a generic "Codumentor" <codumentor@local> identity. It does not push and does not use the user's SCM Push identity.

Push history

The Push dialog includes the authenticated user's recent push operations, including pushes from earlier conversations and after a page reload. Entries are newest first and the dialog loads the last 10 operations (the API accepts a limit of 1–100, default 50).

The history sits in a disclosure that is closed by default and is loaded lazily: the /audit request is made only the first time the user opens it, so opening the dialog costs nothing until the history is actually wanted. The open state is recorded on the element, so a re-render (a commit landing, a turn ending) does not snap it shut. After a successful push the list is refreshed only if it is already open; otherwise the first open fetches it fresh, including the just-completed operation.

History is user-scoped; it is not limited to the currently selected conversation. Each operation is one row showing the repository, target branch, the resulting commit SHA, a commit count, the result, and a timestamp. Expanding a row reveals the list of commits that operation carried — each as an abbreviated SHA and subject — and, for a failed attempt, the bounded Git failure detail. The commit list is captured at push time (the set base..expected_head would transport, using the same effective-base computation as the pending-commit preview) and recorded on the audit row, because the push watermark is overwritten on success and the range cannot be reconstructed afterwards. A failed attempt records the set it tried to push; the row's result is what tells you whether it landed. A reconcile push records the session's own work as its commit list while keeping the host-authored merge commit as the row's resulting SHA.

Rows written before the commit list was recorded (and any operation whose range could not be resolved) show no commit list and no count, rather than implying zero commits. Remote URLs, credential names, and credential values are not exposed in the history response.

The pending-commit badge remains conversation-specific: a history entry does not mean that the current conversation has work waiting to be pushed.

Notes