feat: worktree sessions inherit parent always-allow permissions (#101) #282

Merged
dries merged 6 commits from feat/101-worktree-inherit-permissions into main 2026-07-10 09:31:32 +02:00
Owner

Closes #101.

Worktree/child sessions launched from a parent now inherit the parent's accumulated always-allow approvals, so the user isn't re-prompted for patterns the parent already accepted. Default-on, soft-fails, snapshot at split time.

What changed

  • Capture user-clicked "Allow always" (internal/autoapprove/inherit_capture.go): a bounded asked-cache bridges permission.asked -> permission.replied; on reply == "always" we persist an auto_approved_permission row (once/reject persist nothing). Threaded reply through the whole OnPermissionReplied chain (tee/watcher/session-events).
  • Build inherited ruleset (internal/permissions/inherit.go): maps stored PermissionText+Patterns from ListApprovedPermissions(parent) into the LaunchRequest.PermissionRules shape. Allowlist-gated keys, flat vs patternable handling, dedup, unknown-key drop, 500-pattern cap, empty-parent -> nil.
  • Wire inheritance: mcp/tools_split.go (same-dir + worktree paths) and server/handlers_worktree.go (parentSessionId in body) build the ruleset when the setting is on and pass it via the existing PermissionRules plumbing. Adds permissionsInherited / permissionsInheritedCount / permissionsInheritError to results; never blocks launch.
  • Setting (reuses the setting KV table, no migration): worktree.inherit_permissions, default on; GET/POST /api/settings/worktree-inherit-permissions.
  • Frontend: WorktreeFormModal passes parentSessionId and shows a grey "Will inherit N approved permissions" hint; Settings toggle.
  • Docs: AGENTS.md + session-splitting skill.

Notes / deviations from the issue

  • Issue steps 3 & 4 (OPENCODE_PERMISSION env plumbing + tmux signature refactor) are obsolete — superseded by the LaunchRequest.PermissionRules path added in a prior commit. Built on that instead.
  • Builder lives in a small internal/permissions package rather than inline, for testability.
  • internal/platforms/opencode shows in a raw diff-vs-main only because main advanced; the branch itself does not touch it.

Verification

  • make test — green
  • make lint — 0 errors (platform-branching / host-helper / settings-row checks pass)
  • Branch is behind main by 3 commits; rebase before merge if desired.
Closes #101. Worktree/child sessions launched from a parent now inherit the parent's accumulated always-allow approvals, so the user isn't re-prompted for patterns the parent already accepted. Default-on, soft-fails, snapshot at split time. ## What changed - **Capture user-clicked "Allow always"** (`internal/autoapprove/inherit_capture.go`): a bounded asked-cache bridges `permission.asked` -> `permission.replied`; on `reply == "always"` we persist an `auto_approved_permission` row (`once`/`reject` persist nothing). Threaded `reply` through the whole `OnPermissionReplied` chain (tee/watcher/session-events). - **Build inherited ruleset** (`internal/permissions/inherit.go`): maps stored `PermissionText`+`Patterns` from `ListApprovedPermissions(parent)` into the `LaunchRequest.PermissionRules` shape. Allowlist-gated keys, flat vs patternable handling, dedup, unknown-key drop, 500-pattern cap, empty-parent -> nil. - **Wire inheritance**: `mcp/tools_split.go` (same-dir + worktree paths) and `server/handlers_worktree.go` (`parentSessionId` in body) build the ruleset when the setting is on and pass it via the existing `PermissionRules` plumbing. Adds `permissionsInherited` / `permissionsInheritedCount` / `permissionsInheritError` to results; never blocks launch. - **Setting** (reuses the `setting` KV table, no migration): `worktree.inherit_permissions`, default on; `GET/POST /api/settings/worktree-inherit-permissions`. - **Frontend**: `WorktreeFormModal` passes `parentSessionId` and shows a grey "Will inherit N approved permissions" hint; Settings toggle. - **Docs**: AGENTS.md + session-splitting skill. ## Notes / deviations from the issue - Issue steps 3 & 4 (OPENCODE_PERMISSION env plumbing + tmux signature refactor) are **obsolete** — superseded by the `LaunchRequest.PermissionRules` path added in a prior commit. Built on that instead. - Builder lives in a small `internal/permissions` package rather than inline, for testability. - `internal/platforms/opencode` shows in a raw diff-vs-main only because main advanced; the branch itself does not touch it. ## Verification - `make test` — green - `make lint` — 0 errors (platform-branching / host-helper / settings-row checks pass) - Branch is behind main by 3 commits; rebase before merge if desired.
Persist an ApprovedPermission row when a permission.replied event
carries reply=="always", so the parent's runtime allowlist can later
be inherited by worktree/child sessions (#101). The reply value is
threaded through the SSE tee and a bounded asked-cache bridges the
asked-side patterns to the replied event. once/reject persist nothing.
Map a parent session's accumulated always-allow ApprovedPermission rows
into []platforms.PermissionRule for a worktree/child launch (#101).
Parses the permission key against the OpenCode allowlist, groups and
dedups patterns per key, emits flat allow rules for pattern-less keys,
drops unknown keys, and caps the total at 500.
New worktree.inherit_permissions KV setting (default on) with
Get/SetWorktreeInheritPermissions helpers on the setting table, plus a
GET/POST /api/settings/worktree-inherit-permissions endpoint (#101).
Wire the inherited-ruleset builder into both new_session paths (MCP
same-dir + worktree) and the /wt HTTP flow. When the
worktree.inherit_permissions setting is on and a parent is known, the
child session is seeded with the parent's always-allow rules. Adds
permissionsInherited{,Count} result fields and soft-fails (never blocks
the launch) with permissionsInheritError on any build/apply error (#101).
Thread the current session into the /wt flow as parentSessionId
(command-palette and composer target-selector call sites), show a grey
hint with the count of approvals the new session will inherit, and add
a Settings toggle for the worktree.inherit_permissions setting. Types
gain parentSessionId on the request and permissionsInherited{,Count,
Error} on the response (#101).
docs: note worktree permission inheritance (#101)
All checks were successful
CI / Build Desktop (macOS arm64) (pull_request) Successful in 1m2s
CI / Frontend (pull_request) Successful in 5m13s
CI / Backend (pull_request) Successful in 6m5s
CI / Coverage Ratchet (pull_request) Successful in 8m32s
CI / Build (pull_request) Successful in 3m55s
CI / Playwright E2E (pull_request) Successful in 6m38s
CI / Semantic Tag (pull_request) Has been skipped
931612dfbc
Document the parent->child always-allow inheritance under the Worktrees
and MCP server sections of AGENTS.md, and tell the splitting skill to
surface permissionsInherited{,Count} in its summary.

Coverage ratchet: pass

Suite Baseline This PR Δ
go 72.00% 72.10% +0.10
frontend 61.66% 62.13% +0.47

Tolerance: -0.1%. Baseline stored on gh-pages.

<!-- coverage-ratchet --> ### Coverage ratchet: ✅ pass | Suite | Baseline | This PR | Δ | | |---|---|---|---|---| | go | 72.00% | 72.10% | +0.10 | ✅ | | frontend | 61.66% | 62.13% | +0.47 | ✅ | _Tolerance: -0.1%. Baseline stored on `gh-pages`._
dries merged commit 2b2cd0a4ac into main 2026-07-10 09:31:32 +02:00
dries deleted branch feat/101-worktree-inherit-permissions 2026-07-10 09:31:32 +02:00
Sign in to join this conversation.
No description provided.