Phase 8: Ports, Git, and actions
Implemented
- Added a declared/reserved/bound port domain with accepted-manifest and
Compose provenance, SQLite-backed stopped-project reservations, attributed
runtime bindings, deduplicated
lsofTCP/UDP listeners, stable conflict IDs, wildcard-host reasoning, partial-source warnings, and preferred-range suggestions with exclusions. - Added fresh read-only Git observation using porcelain v2, including branch or detached state, staged/modified/untracked/conflicted counts, ahead/behind, stash count, last commit, remotes, worktrees, and merge/rebase state.
- Added typed and risk-classified manifest actions plus built-in terminal, VS Code, Codex, Claude Code, Git pull, and accepted endpoint actions.
- Added durable
action.runoperations with destructive confirmation, cancellation and timeouts, symlink-aware root containment, explicit outside-root permission, a narrow environment allowlist, bounded optional output capture, explicit shell opt-in, and privilege-escalation rejection. - Added macOS terminal, editor, agent, and HTTP/HTTPS browser launch adapters. Terminal and agent tests assert the exact resolved working directory.
- Added redaction-safe action audit persistence and daemon-restart recovery.
- Added generated OpenAPI Go and TypeScript clients, private IPC clients, CLI commands, a live port registry, and project Git/quick-action UI slices.
- Added one deterministic port-conflict visual reference and extended real browser onboarding coverage through Git, actions, and port evidence.
- Added durable preferred port ranges and exclusions used by browser suggestions, plus durable terminal/editor preferences that filter built-in quick actions without changing explicitly accepted manifest actions.
Files and modules added
internal/ports/{domain,application,adapters}internal/sourcecontrol/{domain,application,adapters}internal/actions/{domain,application,adapters}internal/platform/sqlite/ports.gointernal/platform/sqlite/action_audit.gointernal/transport/cli/developer_commands.gointernal/transport/httpapi/developer_handler.gomigrations/00006_ports_actions.sqlweb/src/domains/portsweb/src/domains/projects/components/ProjectDeveloperTools.vuedocs/architecture/developer-workflows.md
Architecture decisions
No new ADR was required. The implementation realizes the existing modular monolith, manifest precedence, SQLite, local transport security, log/audit redaction, and platform-order decisions. Port, source-control, and action policy remain separate application boundaries. macOS was the first native launcher; Phase 18 subsequently added the Linux and Windows adapters required by ADR-0015.
Action commands are never built by UI or transport code. The generated API submits a durable operation, and the operation executor invokes the trusted action service. This retains one cancellation, serialization, recovery, and event model rather than adding an action-specific job system.
Tests added
- Port declaration/reservation/binding reconciliation, stopped-vs-running conflict visibility, same-project service conflicts, logical-claim de-duplication, wildcard hosts, unknown listeners, listener-row de-duplication, and suggestion exclusions.
- Real temporary Git repository updates after a file change plus porcelain branch/change/commit/worktree parsing.
- Manifest action invariants, destructive confirmation, parent and symlink
escape rejection, explicit escape permission, exact terminal working
directory, command cancellation, environment allowlisting, and
sudo/doasrejection. - SQLite reservation cleanup, audit outcome persistence, and schema migration 6.
- HTTP empty-array contracts and confirmed/unconfirmed action operation paths.
- Vue conflict, Git, action, loading/error, and free-port suggestion states.
- Playwright real-daemon and deterministic visual coverage.
Verification evidence
go test ./internal/actions/... ./internal/manifest/...PASS
go test ./internal/ports/...PASS
make lintPASS: gofmt, go vet, golangci-lint, architecture check, frontend ESLint
pnpm --dir web typecheckpnpm --dir web testPASS
pnpm --dir web test:e2ePASS: 2 real-daemon browser flows
pnpm --dir web test:visualPASS: system and deterministic port-conflict baselines
make buildPASS: production Vue bundle and packaged Go binary
make qualityPASS: generated-code drift, lint, typechecking, unit and race tests, freshdatabase migration, vulnerability scan, isolated browser E2E, visual baselines,and production buildPackaged acceptance used a fresh temporary schema-6 database and private Unix
socket. The CLI trusted the action fixture, returned live Git branch/change and
worktree state, listed the built-in and accepted actions, suggested port
15000/tcp after reading real host listeners, queued verify, and observed
its durable operation reach succeeded. The audit row stored ipc,
read_only, command, and the exact fixture root, with no output or
environment values.
Acceptance criteria status
- A stopped project’s persistent reservation conflicts with another project’s live binding before startup and is rendered with both sources.
- Port evidence identifies accepted manifest, Compose-derived, runtime, and live OS-listener sources without collapsing their fact kinds.
- Git state is read fresh and updates after file or repository changes.
- Manifest approval and execution-time symlink resolution both prevent implicit escape from the trusted project root; execution requires an explicit permission to cross it.
- The macOS terminal adapter is tested to launch at the exact resolved project working directory.
Known limitations and deferred work
- Automatic repository rewriting for port remediation is intentionally out of scope. The registry explains conflicts and suggests a free port only.
- Git commit, merge, rebase, and stash mutation UI is intentionally deferred.
- Action output is bounded for process safety but is not yet a first-class log stream; persistent process and Compose logs continue to use the Phase 7 observability pipeline.
2026-07-17 terminal and endpoint evidence
- Dashboard and project-header Terminal controls use the durable terminal
preference:
integratedopens Switchyard’s terminal tab, while an external preference queues the trustedterminal.openaction. The terminal tab remains directly accessible in either mode. - The explicit external-terminal control is disabled when no trusted
terminal.openaction exists. On macOS, the launcher uses the selected Terminal.app or iTerm2 AppleScript API to open and focus a shell at the exact trusted working directory. Other platforms reject the macOS-only iTerm2 provider instead of silently substituting another terminal. - Every trusted
browser.openaction is rendered as a named project endpoint; browser and terminal actions are excluded from generic quick actions. - Project action failures preserve the server problem detail and code instead of replacing them with a generic queueing message.
2026-07-21 external-terminal preference evidence
- Extended the existing durable terminal preference and generated API enum
with an explicit
itermchoice while preservingintegratedandsystemvalues for existing settings. - Carried the selected provider through the trusted action definition and narrow launcher interface; no UI or transport code constructs a command.
- Added settings validation, generated-contract, provider-routing, exact working-directory, unknown-provider, UI save, and preferred-button tests.
make lint,make typecheck,make test,make test-race,make platform-check,make test-e2e,make test-visual,make repository-check, andmake buildpassed. Generated outputs were byte-identical across consecutive generation runs.