---
name: gosuda-dev-workflow
description: "Build, lint, test, and verification workflow for the portal-tunnel Go+frontend monorepo. Use when setting up the repo, running CI-equivalent checks, or before committing changes."
license: "MIT"
metadata:
  decimalai-skill-id: "98914d5d-9638-4645-aeb1-217f2db43c41"
  decimalai-version-id: "7d1b88f0-e119-48b7-b93f-d3f180230130"
  decimalai-version: "1"
  decimalai-body-sha256: "653b3dcec9f46b13e3acdf12e546ad1046712262fd41be16196c4b54a6989ec5"
  decimalai-name: "gosuda/dev-workflow"
  decimalai-format: "1"
  decimalai-invocation: "model"
  decimalai-category: "devops"
  decimalai-source: "https://app.decimal.ai/s/gosuda-dev-workflow@1/SKILL.md"
---
# portal-tunnel dev workflow

## Toolchain
- Go pinned to `go1.26.4` via `go.mod`; the Makefile exports `GOTOOLCHAIN=go1.26.4`, so `make` targets self-pin even on other Go installs.
- `make install` installs the pinned dev tools: `goimports@v0.41.0` and `golangci-lint@v2.11.1` (into `$(go env GOPATH)/bin`, usually `~/go/bin` — put it on PATH).
- Frontend build/test needs Node 22+.
- `bun` is needed only for `make build-docs`.

## Verification (CI commands, from AGENTS.md)
Run these three before pushing; they are what CI enforces:
```
make vet     # go vet ./cmd/... ./portal/... ./sdk/... ./types/... ./utils/...
make lint    # golangci-lint run (same package set)
make test    # go test -coverprofile=coverage.out (same set) + `cd frontend && npm test` (vitest)
```
Per AGENTS.md, run tests only when needed/asked; `make tidy` is local maintenance, not a CI gate.

## Pre-commit hooks
`.pre-commit-config.yaml` runs whitespace/EOL/BOM fixers, golangci-lint (config-verify, `--new-from-rev HEAD --fix`, and full `--fix`), plus local `make fmt` and `go vet ./...`.
- `make fmt` runs `gofmt -w .` then `goimports -w .`, so `goimports` MUST be on PATH or the commit hook fails with `goimports: No such file or directory` — run `make install` first.
- Note the golangci-lint version differs between pre-commit (`v2.10.1`) and the Makefile (`v2.11.1`); expect minor lint-behavior drift.

## Build
- `make build` → `build-tunnel` + `build-server` (full artifacts).
- `make build-frontend` builds the SPA (`cd frontend && npm ci && npm run build`) and copies `frontend/dist/.` into `cmd/relay-server/dist/app` (embedded by the relay via `//go:embed dist/*`).
- `make build-server-bin` compiles only `bin/relay-server`; it assumes `dist/app` already exists (use after `build-frontend`, or the binary fails at startup asking you to run `make build-frontend`).
- `make build-tunnel` cross-compiles `cmd/portal-tunnel` for linux/darwin/windows × amd64/arm64.
- Shared Go build flags live in the Makefile var `GO_BUILD_FLAGS` (`-trimpath -ldflags "-s -w"`); the Dockerfile calls `make build-server-bin` rather than duplicating them.

## Making changes (AGENTS.md conventions — enforce in review)
- Shared contracts, constants, and **public path strings** belong in `types/` (e.g. `types/paths.go`), never duplicated in runtime/helpers.
- Stateless shared transforms go in `utils/`; stateful/domain logic stays with its real owner (`sdk/` or `portal/`).
- Prefer one owner per contract; no speculative abstraction, wrappers, or dead fields — remove them while touching nearby code.
