Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
Before implementing, review relevant repo skills in .github/skills/ and apply them when the task touches Go, APIs, CRDs, controllers, or platform code.
- Skill use is required when applicable, not optional decoration.
- Multiple skills may apply; combine them when the change crosses concerns.
- For Go work, start with the relevant
acn-go-*skills and follow them.
This repo ships two Go code-intelligence integrations for Copilot CLI / IDE / cloud agents:
.github/lsp.json—goplsas a Language Server (powers/lsp)..mcp.json— the officialgopls mcpserver (gopls v0.20+) exposinggo_search,go_symbol_references,go_package_api,go_file_context,go_workspace,go_diagnostics,go_vulncheck,go_rename_symbol..github/gopls-mcp-instructions.md— gopls' own model-instructions file describing the read/edit workflow. Treat it as required reading when working with Go in this repo.
One-time setup per developer: run ./.github/scripts/setup-go-tooling.sh (installs gopls; it must end up on PATH). Then /exit and re-launch Copilot CLI.
When working with Go code you must prefer code-intelligence tools over plain text search:
- Symbol lookup (definition, references, implementations, type info, hover/docs, workspace/document symbols) → use the LSP or gopls MCP tools. Do not grep for these.
- Call sites of a function, method, or interface → LSP
referencesorgo_symbol_references(andimplementationsfor interfaces). Not grep. - "Where is this defined?" → LSP
definitionorgo_search. Not grep + view. - Package surface area →
go_package_apior LSP document symbols. Not view + scan. - Build / type errors →
go_diagnostics. Notgo buildparsing. - Run
/lspand/mcpin Copilot CLI to confirm the servers are healthy. If the relevant server is unavailable, fix it before falling back to text search.
grep and view remain appropriate for: raw string/comment search, non-Go files (YAML, Markdown, shell, manifests), build/CI configs, log lines, and any case the LSP/MCP demonstrably cannot answer. State the reason briefly when falling back.
Don't assume. Don't hide confusion. Surface tradeoffs.
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
Minimum code that solves the problem. Nothing speculative.
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
Touch only what you must. Clean up only your own mess.
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
Define success criteria. Loop until verified.
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
AI agents never author public communication as maintainers or contributors.
This rule is mandatory and non-overridable for every agent, subagent, scheduled task, tool, and workflow in this repository. Agents must never post comments, replies, reviews, reactions, or discussion messages; resolve or dismiss review threads; or edit, close, reopen, lock, or otherwise change public pull request or issue text/metadata. Draft the exact text for a human maintainer instead.
Keep read-only GitHub capabilities available. Read-only gh, API, MCP, and web
research is allowed.
Branch pushes are allowed and are not a public communication surface. Pull request creation and workflow/release mutations are allowed only when the current user task explicitly requests that public action. Do not infer authorization from a general request to implement, fix, review, or validate code.
Exception: if
$GITHUB_WORKSPACEis set, work there instead of a worktree — only that path is committed and pushed, so edits made anywhere else are silently discarded. The rest of this section applies to local sessions.
All local agent work MUST happen in a dedicated git worktree, never in the shared repo root. The repo root is shared across concurrent agent sessions; mutating it causes parallel branches, working trees, and build artifacts (output/, bin/) to collide. From the repo root, create a worktree under the active session folder before touching files:
SLUG=<short-task-slug> # e.g. cns-shutdown
BRANCH=<branch-name> # e.g. fix/cns-shutdown
WT="$HOME/.copilot/session-state/$COPILOT_AGENT_SESSION_ID/files/worktrees/$SLUG"
git fetch origin master --quiet
git worktree add -b "$BRANCH" "$WT" origin/master
cd "$WT" # then run all subsequent commands hereRun all edits, builds, tests, local commits, and branch pushes from inside $WT.
Unless the current task explicitly requests publishing beyond a branch push,
stop before PR creation or workflow/release mutation and provide the maintainer
with the exact commands. Always stop before public comments, replies, reviews,
thread resolution, or PR/issue text edits; those remain human-only even when
publishing is requested.
After the maintainer confirms that the work was merged or abandoned, prune with
git worktree remove "$WT" and git branch -D "$BRANCH". The shared root is
read-only — only use it for inspection (e.g. git worktree list, git fetch),
never to edit, build, or commit. (Again: this read-only rule is for local CLI
sessions. The cloud coding agent must edit, build, and commit directly in
$GITHUB_WORKSPACE.)
Go-specific notes (no per-worktree install):
- The Go module cache (
$(go env GOMODCACHE), default$GOPATH/pkg/mod) and build cache ($(go env GOCACHE)) are content-addressed and concurrent-safe; Go uses file locking. Do not try to isolate them per worktree. - Module dependencies are fetched lazily by
go build/go test/go mod download. There is nonpm installstep to run aftergit worktree add. output/andbin/(seeMakefile:OUTPUT_DIR = $(REPO_ROOT)/output) are repo-relative, so they're naturally per-worktree. No special handling needed.- LSP / MCP and worktrees — the LSP and
gopls mcpservers are spawned from the cwd Copilot CLI was launched in (typically the shared repo root), not from$WT. That is fine: pass absolute paths under$WTto LSP/MCP tools (go_diagnostics,go_file_context,go_symbol_references, etc.) and gopls discovers the correctgo.modper-file. Do not try to "relaunch from inside the worktree" — the running session can't restart itself, and.github/lsp.json/.mcp.jsonare identical between root and worktree anyway. If you edit either of those config files inside$WT, run/mcpor restart the CLI in a new session to pick up the change.
Multiple agent sessions can — and routinely do — run against this repo on the same machine at the same time. A worktree isolates files and output/, but several other resources are machine-global and will collide if two agents reach for them at once. Before running anything that listens on a port, mutates a shared cache, or starts a long-lived process, isolate or coordinate it.
Specifically for ACN work:
KUBECONFIG— never write to~/.kube/configfrom agent flows. Setexport KUBECONFIG="$WT/.kube/config"(or under the session folder) before anyaz aks get-credentials/kind create cluster/kubectlcall. Do not merge into the shared kubeconfig.- kind clusters —
kind create clusterdefaults to the cluster namekind, which collides across agents. Always pass--name "acn-${COPILOT_AGENT_SESSION_ID:0:8}-$SLUG"and tear it down with the matchingkind delete cluster --name ...when done. - Docker / containerd — container names and image tags are global. Tag built images with a session-derived suffix (e.g.
azure-cns:dev-${COPILOT_AGENT_SESSION_ID:0:8}) and clean them up at the end. Do not reuse the bare:latest/:devtags concurrent agents may also be writing to. - Test ports — CNS HTTP API (default
:10090), Prometheus metrics (:9090), and any Ginkgo/httptestserver defaults will collide. Prefer:0(kernel-assigned) in test fixtures or pass an explicit per-session port; never assume the default port is free. - Long-lived processes —
cnetz,dropgz, the CNS daemon,tcpdump, etc. started by an agent must be tracked by PID and killed at end of session. Usekill <PID>with a specific PID; do not use name-based killers (pkill,killall) — they will hit other agents' processes. - Cloud resources — if a flow provisions AKS clusters, resource groups, or shared images, scope the resource-group name with the session ID and delete the RG at the end. Never reuse a long-lived cluster across agents without coordination.
These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.