Skip to content

feat: add v2 socket methods for full v1 parity#70

Merged
arzafran merged 1 commit into
mainfrom
feat/v2-parity-methods
Jul 8, 2026
Merged

feat: add v2 socket methods for full v1 parity#70
arzafran merged 1 commit into
mainfrom
feat/v2-parity-methods

Conversation

@arzafran

@arzafran arzafran commented Jul 8, 2026

Copy link
Copy Markdown
Member

What this does

Adds the v2 JSON-RPC socket methods that were still missing v1 equivalents, so a
follow-up PR can delete the old line protocol. This PR only adds new v2 methods —
it does not delete or change any v1 handler's behavior.

Summary

v2 method v1 counterpart Threading
surface.report_pwd report_pwd off-main parse + main.async mutate
surface.report_shell_state report_shell_state off-main parse + main.async mutate (reuses SocketFastPathState dedup)
surface.report_git_branch report_git_branch off-main parse + main.async mutate
surface.clear_git_branch clear_git_branch off-main parse + main.async mutate
surface.report_pr report_pr / report_review off-main parse + main.async mutate
surface.clear_pr clear_pr off-main parse + main.async mutate
surface.report_ports report_ports off-main parse + main.async mutate
surface.clear_ports clear_ports off-main parse + main.async mutate
workspace.set_status set_status off-main parse + main.async mutate
workspace.clear_status clear_status off-main parse + main.async mutate
workspace.list_status list_status v2MainSync (exact-snapshot read)
workspace.log log off-main parse + main.async mutate
workspace.clear_log clear_log off-main parse + main.async mutate
workspace.list_log list_log v2MainSync (exact-snapshot read)
workspace.set_progress set_progress off-main parse + main.async mutate
workspace.clear_progress clear_progress off-main parse + main.async mutate
workspace.sidebar_state sidebar_state v2MainSync (exact-snapshot read)
workspace.clear_agent_pid clear_agent_pid off-main parse + main.async mutate
app.reload_config reload_config v2MainSync (matches v1's own implementation, not a telemetry hot path)

Already had v2 equivalents before this PR (verified against the dispatch table, not touched):

  • surface.report_tty (v1 report_tty)
  • surface.ports_kick (v1 ports_kick)

Each new method is a thin adapter that parses params with the existing v2 helpers
(v2String/v2UUID/v2Bool/v2Int), resolves the workspace/surface by explicit
handle the same way v2SurfaceReportTTY/v2SurfacePortsKick do, and then calls the
exact same TabManager/Workspace model APIs the v1 handler calls (e.g.
updateSurfaceDirectory, updateSurfaceGitBranch, updatePanelPullRequest, the
shouldReplace* dedup helpers, SocketFastPathState) — so v1 and v2 produce
identical model mutations without any v1 function body being touched.

Telemetry methods (report_*/ports/log/progress/status) follow the socket command
threading policy: params are parsed and validated off-main, then the actual model
mutation is dispatched with DispatchQueue.main.async and the call returns an
optimistic ok result immediately (mirroring v1's own fire-and-forget "OK" behavior
for these same commands). Read-only exact-snapshot queries (list_status,
list_log, sidebar_state) use the existing v2MainSync pattern shared by sibling
v2 read methods.

docs/v2-api-migration.md is updated with the new method mappings and a note that
v1 removal is planned in a follow-up PR once consumers (shell integration, tests/)
migrate to v2.

Test Plan

  • PROGRAMA_SKIP_ZIG_BUILD=1 xcodebuild -scheme programa -configuration Debug build → BUILD SUCCEEDED
  • PROGRAMA_SKIP_ZIG_BUILD=1 xcodebuild -scheme programa-unit -configuration Debug build-for-testing → TEST BUILD SUCCEEDED
  • Follow-up PRs will migrate v1 consumers to these v2 methods, then delete the v1 line protocol

Adds the remaining v2 JSON-RPC methods needed before the v1 line protocol
can be deleted (PR 1 of the v1-deletion sequence). This PR only adds new
v2 adapters; no v1 handler is modified.

New surface telemetry methods (off-main parse, main.async mutate, per the
socket command threading policy):
- surface.report_pwd            <- v1 reportPwd
- surface.report_shell_state    <- v1 reportShellState (reuses SocketFastPathState dedup)
- surface.report_git_branch     <- v1 reportGitBranch
- surface.clear_git_branch      <- v1 clearGitBranch
- surface.report_pr             <- v1 reportPullRequest
- surface.clear_pr              <- v1 clearPullRequest
- surface.report_ports          <- v1 reportPorts
- surface.clear_ports           <- v1 clearPorts

New workspace-scoped sidebar metadata methods (mutations off-main + main.async;
reads use v2MainSync like sibling read methods):
- workspace.set_status          <- v1 set_status
- workspace.clear_status        <- v1 clear_status
- workspace.list_status         <- v1 list_status
- workspace.log                 <- v1 log
- workspace.clear_log           <- v1 clear_log
- workspace.list_log            <- v1 list_log
- workspace.set_progress        <- v1 set_progress
- workspace.clear_progress      <- v1 clear_progress
- workspace.sidebar_state       <- v1 sidebar_state
- workspace.clear_agent_pid     <- v1 clear_agent_pid

New app method:
- app.reload_config             <- v1 reload_config (uses v2MainSync, matching v1's own call)

Already had v2 equivalents before this PR (verified, not touched):
- surface.report_tty  <- v1 report_tty
- surface.ports_kick  <- v1 ports_kick

Each new method is a thin adapter: parses params with the v2 helpers, resolves
workspace/surface by explicit handle the way v2SurfaceReportTTY/v2SurfacePortsKick
do, then calls the same TabManager/Workspace APIs the v1 handler calls
(updateSurfaceDirectory, updateSurfaceGitBranch, updatePanelPullRequest,
shouldReplace* dedup helpers, etc.) so behavior matches v1 without touching any
v1 function body.

Updated docs/v2-api-migration.md with the new method mappings and a note that
v1 removal is planned in a follow-up PR once consumers migrate.
@arzafran arzafran merged commit 475aa2f into main Jul 8, 2026
8 checks passed
arzafran added a commit that referenced this pull request Jul 8, 2026
…ath (#72)

* fix: mismatched brace/paren in _cmux_start_pr_poll_loop broke bash sourcing

_cmux_start_pr_poll_loop opened its backgrounded PR-poll loop with a bare
`{` but closed it with `)` (typo introduced in 88d6a2f, March 2026).
A `{ ... }` group must close with `}`; mixing it with `)` is a syntax
error, and bash apparently fails the syntax check for the entire file, not
just the malformed function — sourcing programa-bash-integration.bash in a
live shell (bash 3.2 and 5.3 both tested) aborts entirely with 'syntax
error near unexpected token )', so no shell-integration function gets
defined at all.

Found while running `bash -n` as part of verifying an unrelated change
(the v1->v2 JSON-RPC migration in the following commits); confirmed
present on unmodified origin/main via `git show HEAD`. Fixed by opening
a subshell with `(` instead, matching the `( ... ) &` backgrounding
pattern already used elsewhere in this same file (e.g.
_cmux_run_pr_probe_with_timeout, _cmux_prompt_command's pwd/git-branch
probes) — subshells support $! for the backgrounded PID same as job
groups do.

* refactor: shell integration speaks v2 JSON-RPC on the direct socket path

Step 3 of the v1-deletion sequence (see docs/v2-api-migration.md). Converts
the direct-socket sends in the zsh and bash shell-integration scripts from
v1 space-delimited lines to single-line v2 JSON-RPC frames, mirroring the
pattern the REMOTE-relay path in the same scripts already uses
(_cmux_relay_rpc "surface.report_tty" etc., added in PR #70's v2 parity
methods, commit 475aa2f).

Verbs converted (both scripts, kept behaviorally identical to each other):
- report_tty       -> surface.report_tty       {workspace_id, tty_name, surface_id?}
- report_shell_state -> surface.report_shell_state {workspace_id, surface_id, state}
- report_pwd       -> surface.report_pwd       {workspace_id, surface_id, path}
- report_git_branch -> surface.report_git_branch {workspace_id, surface_id, branch, dirty}
- clear_git_branch -> surface.clear_git_branch {workspace_id, surface_id}
- report_pr        -> surface.report_pr        {workspace_id, surface_id, number, url, state, branch}
- clear_pr         -> surface.clear_pr         {workspace_id, surface_id}
- ports_kick       -> surface.ports_kick       {workspace_id, surface_id?, reason}

workspace_id/surface_id are sourced from the same env vars the v1 fast path
and the v2 relay path already use (PROGRAMA_WORKSPACE_ID falling back to
PROGRAMA_TAB_ID, and PROGRAMA_PANEL_ID) via the existing _cmux_relay_workspace_id
helper. report_tty preserves the v1 tmux special case of omitting surface_id.

Both scripts reuse the existing _cmux_json_escape helper (already used by the
relay path) to safely escape quotes/backslashes/control chars in pwd paths and
branch names before interpolating them into the JSON frames.

Fire-and-forget semantics are unchanged: _cmux_send/_cmux_send_bg never read a
response today and still don't after this change; call sites that were
synchronous (report_git_branch/clear_git_branch/report_pr, which already run
inside an already-backgrounded subshell) stay synchronous, and call sites that
were backgrounded stay backgrounded.

The relay path itself is untouched.
@arzafran arzafran deleted the feat/v2-parity-methods branch July 10, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant