Phase 8/13 — Conventions

Coding Conventions & Style Patterns

Every coding convention and style pattern enforced or observed in the copilot-sdk polyglot repository (TypeScript Python Go C#). Each rule is cited with file:line references drawn from the actual codebase.

1. Naming Conventions

1.1 TypeScript (Node.js)

ElementConventionExampleCitation
ClassesPascalCaseCopilotClient, CopilotSessionclient.ts:134, session.ts:59
Interfaces / TypesPascalCaseCopilotClientOptions, SessionConfigtypes.ts:16, types.ts:121
Type aliases (unions)PascalCaseSessionEvent, ToolResulttypes.ts:11, types.ts:130
MethodscamelCasecreateSession, sendAndWaitsession.ts:120, session.ts:154
Private fieldscamelCase (no prefix)this.connection, this.sessionsclient.ts:136
Internal methods_ prefix_dispatchEvent, _handleBroadcastEventsession.ts:293, session.ts:325
ConstantsUPPER_SNAKE_CASEMIN_PROTOCOL_VERSIONclient.ts:56
Fileskebab-caseclient.ts, session-events.tsdirectory listing
Unused variables_ prefix_error, _rpceslint.config.js:23-26
Generic type paramsSingle uppercase / PascalCase<T>, <K extends SessionEventType>session.ts:230

1.2 Python

ElementConventionExampleCitation
ClassesPascalCaseCopilotClient, CopilotSessionclient.py:78, session.py:46
Methodssnake_casecreate_session, send_and_waitsession.py:118, session.py:153
Private methodsLeading __dispatch_event, _register_toolssession.py:248, session.py:274
Private instance varsLeading _self._client, self._event_handlerssession.py:87-88
ConstantsUPPER_SNAKE_CASEMIN_PROTOCOL_VERSIONclient.py:55
Type aliasesPascalCaseConnectionState, LogLeveltypes.py:24
TypedDictsPascalCaseCopilotClientOptions, MessageOptionstypes.py:73
DataclassesPascalCaseToolResult, ToolInvocationtypes.py:122
Filessnake_caseclient.py, session_events.pydirectory listing
Module docstringsPresent on all modulesTriple-quoted at file topclient.py:1-12

1.3 Go

ElementConventionExampleCitation
Exported typesPascalCaseClient, Sessionclient.go:73, session.go:51
Exported methodsPascalCaseSend, SendAndWaitsession.go:113, session.go:160
Unexported helperscamelCasenewSession, registerToolssession.go:80, session.go:265
Struct fields (exported)PascalCaseSessionID, RPCsession.go:53
Struct fields (unexported)camelCaseworkspacePath, clientsession.go:55-59
ConstantsPascalCase prefixedStateDisconnected, AssistantMessagetypes.go:12-15
Enum-style typesPascalCase suffixConnectionState, SessionEventTypetypes.go:9
Filessnake_caseclient.go, session.godirectory listing
Mutex fieldsShort suffixsessionsMux, toolHandlersMclient.go:82, session.go:60-64
Pointer helper fnsSingle-word PascalCaseBool, String, Float64types.go:69-83

1.4 C# (.NET)

ElementConventionExampleCitation
ClassesPascalCaseCopilotClient, CopilotSessionClient.cs:55, Session.cs:53
Public methodsPascalCase + AsyncSendAsync, SendAndWaitAsyncSession.cs:140
Public propertiesPascalCaseSessionId, WorkspacePathSession.cs:75-89
Private fields_camelCase_toolHandlers, _rpcSession.cs:62-69
ConstantsPascalCaseMinProtocolVersionClient.cs:60
EnumsPascalCase membersConnectionState.DisconnectedTypes.cs:19-33
Internal methodsPascalCaseDispatchEvent, RegisterToolsSession.cs:275
RecordsPascalCaseSendMessageRequestSession.cs:754-765
NamespaceDotted PascalCaseGitHub.Copilot.SDKSession.cs:12
FilesPascalCaseClient.cs, Session.csdirectory listing
Naming Convention Comparison Across SDKs
flowchart LR
  subgraph TS["TypeScript"]
    direction TB
    TS1["Classes: PascalCase"]
    TS2["Methods: camelCase"]
    TS3["Private: _prefix"]
    TS4["Constants: UPPER_SNAKE"]
    TS5["Files: kebab-case"]
    TS1 --- TS2 --- TS3 --- TS4 --- TS5
  end
  subgraph PY["Python"]
    direction TB
    PY1["Classes: PascalCase"]
    PY2["Methods: snake_case"]
    PY3["Private: _prefix"]
    PY4["Constants: UPPER_SNAKE"]
    PY5["Files: snake_case"]
    PY1 --- PY2 --- PY3 --- PY4 --- PY5
  end
  subgraph GO["Go"]
    direction TB
    GO1["Types: PascalCase"]
    GO2["Methods: PascalCase"]
    GO3["Private: camelCase"]
    GO4["Constants: PascalCase"]
    GO5["Files: snake_case"]
    GO1 --- GO2 --- GO3 --- GO4 --- GO5
  end
  subgraph CS["C#"]
    direction TB
    CS1["Classes: PascalCase"]
    CS2["Methods: PascalCase+Async"]
    CS3["Private: _camelCase"]
    CS4["Constants: PascalCase"]
    CS5["Files: PascalCase"]
    CS1 --- CS2 --- CS3 --- CS4 --- CS5
  end
          

2. File Organization

2.1 TypeScript Internal Structure

TypeScript File Ordering
flowchart TD
  A["1. Copyright header #40;3 lines#41;"] --> B["2. Module JSDoc #40;@module tag#41;"]
  B --> C["3. Imports #40;node: → third-party → local#41;"]
  C --> D["4. Module-level constants"]
  D --> E["5. Helper functions #40;private#41;"]
  E --> F["6. Main export class #40;single per file#41;"]
  F --> G["7. Type exports / re-exports"]
          

Citation: nodejs/src/client.ts:1-56, nodejs/src/session.ts:1-32

2.2 Python Internal Structure

Python File Ordering
flowchart TD
  A["1. Module docstring"] --> B["2. Standard library imports"]
  B --> C["3. Third-party imports"]
  C --> D["4. Local imports"]
  D --> E["5. Module-level constants"]
  E --> F["6. Private helper functions"]
  F --> G["7. Main class #40;single per file#41;"]
          

Citation: python/copilot/client.py:1-55, python/copilot/session.py:1-44

2.3 Go Internal Structure

Go File Ordering
flowchart TD
  A["1. Package doc comment"] --> B["2. package declaration"]
  B --> C["3. import #40;...#41; block"]
  C --> D["4. Unexported types #40;helpers#41;"]
  D --> E["5. Exported type + doc comment"]
  E --> F["6. Constructor function"]
  F --> G["7. Exported methods"]
  G --> H["8. Unexported methods"]
          

Citation: go/session.go:1-89, go/client.go:1-52

2.4 C# Internal Structure

C# File Ordering
flowchart TD
  A["1. Copyright header"] --> B["2. Using directives"]
  B --> C["3. Namespace declaration #40;file-scoped#41;"]
  C --> D["4. XML doc summary"]
  D --> E["5. Class declaration #40;sealed partial#41;"]
  E --> F["6. Private fields → Properties → Constructor"]
  F --> G["7. Public methods → Internal → Private"]
  G --> H["8. Nested records/types #40;DTOs#41;"]
          

Citation: dotnet/src/Session.cs:1-100, dotnet/src/Session.cs:754-811

3. Error Handling Conventions

3.1 TypeScript

  • Throwing: throw new Error("descriptive message") for domain errors
  • Catch-and-rethrow: Catch specific types (ConnectionError, ResponseError), rethrow unknown, swallow connection-lost
  • Unused caught errors: Named _error per ESLint rule
  • Timeout errors: Created inline with template literals
  • Handler errors: Silently caught in event dispatchers

Citations: session.ts:177-179, session.ts:303-306, session.ts:378-388

3.2 Python

  • Raising: raise ValueError(...) for validation, raise RuntimeError(...) for system failures
  • Catch pattern: Broad except Exception with pylint disable, narrow catches for RPC
  • Handler isolation: Exceptions caught and printed to stderr
  • Deprecation: warnings.warn("...", DeprecationWarning, stacklevel=2)

Citations: client.py:142, session.py:269-272, session.py:680-686

3.3 Go

  • Error wrapping: fmt.Errorf("failed to send message: %w", err) — always uses %w
  • Panic for programmer errors: panic("CLIUrl is mutually exclusive...")
  • Recover in dispatchers: defer func() { if r := recover(); r != nil { ... } }()
  • Multi-return: All fallible public methods return (value, error)
  • Nil checks: Pointer fields checked before dereference

Citations: session.go:123, client.go:141, session.go:454-462

3.4 C#

  • Exception types: ArgumentException, InvalidOperationException, TimeoutException, ObjectDisposedException
  • Null-coalescing throw: ?? throw new InvalidOperationException(...)
  • Catch-and-swallow: Catches IOException / ObjectDisposedException for connection loss
  • Pattern matching: switch (evt) { case AssistantMessageEvent: ... }

Citations: Client.cs:124, Session.cs:508, Session.cs:447-454

Error Handling Strategy Comparison
flowchart LR
  subgraph TS["TypeScript"]
    direction TB
    TS1["throw new Error#40;#41;"]
    TS2["catch + rethrow"]
    TS3["Swallow in dispatchers"]
    TS4["_error for unused"]
    TS1 --- TS2 --- TS3 --- TS4
  end
  subgraph PY["Python"]
    direction TB
    PY1["raise ValueError/RuntimeError"]
    PY2["except Exception #40;broad#41;"]
    PY3["Print to stderr"]
    PY4["warnings.warn#40;#41;"]
    PY1 --- PY2 --- PY3 --- PY4
  end
  subgraph GO["Go"]
    direction TB
    GO1["fmt.Errorf#40;%w#41;"]
    GO2["#40;value, error#41; return"]
    GO3["defer recover#40;#41;"]
    GO4["panic#40;#41; for config bugs"]
    GO1 --- GO2 --- GO3 --- GO4
  end
  subgraph CS["C#"]
    direction TB
    CS1["Typed exceptions"]
    CS2["?? throw pattern"]
    CS3["Catch IOException"]
    CS4["Pattern matching"]
    CS1 --- CS2 --- CS3 --- CS4
  end
          

4. Documentation Style

4.1 TypeScript — JSDoc

  • Module-level: /** @module client */ after copyright header
  • Class-level: Multi-line /** ... */ with @example blocks
  • Method-level: @param, @returns, @throws, @example
  • Internal marker: @internal
  • Deprecation: @deprecated Use {@link disconnect} instead.
  • Cross-references: {@link CopilotClient.createSession}

4.2 Python — Docstrings (Google style)

  • Module-level: Triple-quoted at file top
  • Class-level: Multi-line with Attributes: and Example: sections
  • Method-level: Sections: summary, Args:, Returns:, Raises:, Example:, Note:
  • Deprecation: .. deprecated:: reST directive
  • Cross-references: Sphinx-style :meth: / :class:

4.3 Go — Go Doc Comments

  • Package-level: // Package copilot provides a Go SDK for ...
  • Type-level: // Session represents a single conversation ...
  • Method-level: // Send sends a message ... with indented code examples
  • Cross-references: Go doc link syntax [Client.CreateSession]
  • Deprecated: // Deprecated: Use [Session.Disconnect] instead.

4.4 C# — XML Documentation

  • <summary> — Required on all public/internal members
  • <remarks> — Multi-<para> blocks for extended descriptions
  • <example><code> — Inline code examples
  • <param>, <returns>, <exception cref="...">
  • <see cref="..."> — Cross-references
  • <value> — Property documentation

5. Type System Usage

5.1 TypeScript

  • Strict mode: "strict": true in tsconfig
  • Discriminated unions: SessionEvent discriminated on type field
  • Generics: Tool<TArgs>, ToolHandler<TArgs>
  • type vs interface: interface for object shapes, type for unions
  • Null handling: | undefined + strict null checks
  • Record<string, unknown> preferred over any
  • unknown preferred over any for function args

5.2 Python

  • Type annotations: Full hints with Python 3.11+ syntax (str | None)
  • from __future__ import annotations for forward references
  • TypedDict with total=False for optional fields
  • @dataclass for value objects
  • Literal for string union types
  • Callable + Awaitable for sync/async flexibility

5.3 Go

  • Pointer for optional: *bool, *string
  • Pointer helpers: Bool(v bool) *bool
  • JSON struct tags: json:"fieldName,omitempty"
  • Type aliases for enums: type ConnectionState string
  • any for untyped JSON, json.RawMessage for deferred parsing

5.4 C#

  • Nullable reference types: string? (project-wide enabled)
  • sealed partial class for source generation
  • Records for internal DTOs
  • Enums with JSON mapping: [JsonStringEnumMemberName]
  • IAsyncDisposable for cleanup
  • Source-generated JSON: [JsonSerializable] for AOT
  • volatile for thread-safe handler fields

6. Async Patterns

6.1 TypeScript — async/await + Promises

  • All I/O methods are async returning Promise<T>
  • sendAndWait: Promise.race([idlePromise, timeoutPromise])
  • Timeout: new Promise<never>((_, reject) => { setTimeout(...) })
  • Cleanup: finally block clears timeout and unsubscribes
  • Subscribe-before-send: Avoids race conditions

6.2 Python — asyncio

  • All I/O methods are async def
  • send_and_wait: asyncio.Event() + asyncio.wait_for()
  • Fire-and-forget: asyncio.ensure_future(...)
  • Dual handlers: inspect.isawaitable(result) for sync/async
  • Thread safety: threading.Lock() for handler sets

6.3 Go — Goroutines + Channels

  • Public methods accept context.Context first
  • SendAndWait: context.WithTimeout, buffered channels, select
  • Fire-and-forget: go s.executeToolAndRespond(...)
  • Synchronization: sync.Mutex / sync.RWMutex / sync.atomic
  • Default timeout: 60 seconds

6.4 C# — Task-based Async

  • All async methods: MethodNameAsync, return Task<T>
  • CancellationToken as last parameter with default
  • SendAndWaitAsync: TaskCompletionSource<T>
  • Disposal: IAsyncDisposable + Interlocked.Exchange
  • Fire-and-forget: async void for broadcast events
  • Concurrent collections: ConcurrentDictionary
Async Pattern Comparison
flowchart LR
  subgraph TS["TypeScript"]
    direction TB
    TS1["async/await"]
    TS2["Promise.race#40;#41;"]
    TS3["setTimeout"]
    TS4["finally cleanup"]
    TS1 --- TS2 --- TS3 --- TS4
  end
  subgraph PY["Python"]
    direction TB
    PY1["async def / await"]
    PY2["asyncio.Event#40;#41;"]
    PY3["asyncio.wait_for#40;#41;"]
    PY4["threading.Lock#40;#41;"]
    PY1 --- PY2 --- PY3 --- PY4
  end
  subgraph GO["Go"]
    direction TB
    GO1["goroutines"]
    GO2["channels + select"]
    GO3["context.WithTimeout"]
    GO4["sync.Mutex"]
    GO1 --- GO2 --- GO3 --- GO4
  end
  subgraph CS["C#"]
    direction TB
    CS1["async Task#60;T#62;"]
    CS2["TaskCompletionSource"]
    CS3["CancellationToken"]
    CS4["IAsyncDisposable"]
    CS1 --- CS2 --- CS3 --- CS4
  end
          

7. Import/Export Patterns

7.1 TypeScript

  • Barrel file: nodejs/src/index.ts re-exports all public API
  • Named exports only — no default exports
  • export type for type-only re-exports
  • Import groups: (1) node:, (2) vscode-jsonrpc, (3) local ./generated/, (4) local ./types.js
  • .js extensions on all local imports (ESM)
  • import type when only types needed

7.2 Python

  • __init__.py barrel with explicit __all__
  • Import groups: (1) from __future__, (2) stdlib, (3) third-party, (4) relative
  • Relative imports: from .module import Name
  • TYPE_CHECKING guard for circular import avoidance

7.3 Go

  • Single import block grouped: stdlib, blank, third-party, blank, internal
  • Package = directory: package copilot / package rpc
  • Internal packages: go/internal/jsonrpc2, go/internal/embeddedcli
  • Exported via capitalization — no explicit export list

7.4 C#

  • File-scoped namespaces: namespace GitHub.Copilot.SDK;
  • Using directives: System, Microsoft, third-party, project
  • sealed partial class — sealed + partial
  • internal visibility for SDK internals

8. Code Generation Conventions

8.1 Generated File Markers

LanguageMarkerSourceCitation
TS/** AUTO-GENERATED FILE - DO NOT EDIT */api.schema.jsongenerated/rpc.ts:1-4
Python""" AUTO-GENERATED FILE - DO NOT EDIT """Samegenerated/rpc.py:1-4
Go// AUTO-GENERATED FILE - DO NOT EDITSamerpc/generated_rpc.go:1-2
C#// AUTO-GENERATED FILE - DO NOT EDITSameGenerated/Rpc.cs:5-6

8.2 Generated File Locations

LanguageRPC TypesSession Events
TSnodejs/src/generated/rpc.tsnodejs/src/generated/session-events.ts
Pythonpython/copilot/generated/rpc.pypython/copilot/generated/session_events.py
Gogo/rpc/generated_rpc.gogo/generated_session_events.go
C#dotnet/src/Generated/Rpc.csdotnet/src/Generated/SessionEvents.cs

8.3 Generated Code Exclusions

  • ESLint: **/generated/** in ignores
  • Ruff: "generated" excluded
  • golangci-lint: generated path excluded
  • Go formatting: find . -name "*.go" -not -path "*/generated/*"

8.4 Generation Tooling

Code Generation Pipeline
flowchart TD
  A["JSON Schemas#60;br/#62;api.schema.json + session-events.schema.json"] --> B["generate-session-types.ts #40;quicktype#41;"]
  B --> C["rpc.ts"]
  B --> D["rpc.py"]
  B --> E["generated_rpc.go"]
  B --> F["Rpc.cs"]
          

9. Linting & Formatting Rules

9.1 TypeScript (ESLint + Prettier)

ESLint

  • Parser: @typescript-eslint/parser, ECMAScript 2022, ESM
  • no-unused-vars: error (ignores _-prefixed)
  • no-explicit-any: warn
  • no-console: off
  • Ignores: dist/, node_modules/, *.config.ts, **/generated/**

Prettier Settings

SettingValue
Semicolonsyes
Trailing commasES5 style
Quotesdouble
Print width100
Tab width4 spaces
Arrow parensalways
Line endingsLF only

9.2 Python (Ruff)

SettingValue
Line length100
Target versionpy311
Selected rulesE, W, F, I, UP
Formatdouble quotes, spaces
Excludesgenerated/

9.3 Go (golangci-lint + gofmt)

SettingValue
Timeout5 minutes
Enabledgovet, ineffassign, staticcheck
Disablederrcheck (explicit)
Excludedgenerated/

9.4 C# (dotnet format)

  • Formatting: dotnet format
  • CI: dotnet format --verify-no-changes
  • AOT: <IsAotCompatible>true</IsAotCompatible>
  • Docs: <GenerateDocumentationFile>true</GenerateDocumentationFile>

10. Cross-Language Consistency Rules

10.1 API Shape Parity

ConceptTSPythonGoC#
ClientCopilotClientCopilotClientClientCopilotClient
SessionCopilotSessionCopilotSessionSessionCopilotSession
CreatecreateSession()create_session()CreateSession()CreateSessionAsync()
Sendsend()send()Send()SendAsync()
Send+WaitsendAndWait()send_and_wait()SendAndWait()SendAndWaitAsync()
Subscribeon()on()On()On()
MessagesgetMessages()get_messages()GetMessages()GetMessagesAsync()
Disconnectdisconnect()disconnect()Disconnect()DisposeAsync()
CLI URLcliUrlcli_urlCLIUrlCliUrl

10.2 Shared Protocol Constants

MIN_PROTOCOL_VERSION = 2

All four SDKs define the same minimum protocol version (value: 2), ensuring wire-level compatibility.

10.3 Generated Types from Shared Schemas

All SDKs generate RPC types from the same JSON schemas:

  • api.schema.json → rpc types in all 4 languages
  • session-events.schema.json → event types in all 4 languages

10.4 Consistent Behavioral Patterns

  1. Event dispatch: All SDKs intercept external_tool.requested and permission.requested broadcast events
  2. Handler isolation: Errors don't propagate from handlers to dispatcher
  3. Unsubscribe pattern: on() returns cleanup (TS: () => void, Python: Callable, Go: func(), C#: IDisposable)
  4. Resource cleanup: All SDKs clear handlers on disconnect
  5. Deprecation: destroy() deprecated for disconnect() in TS, Python, Go

10.5 CLI URL Naming

CLI URL Naming Per Language
flowchart LR
  A["Node: cliUrl"] --- B["Python: cli_url"] --- C["Go: CLIUrl"] --- D[".NET: CliUrl"]
          

10.6 Monorepo Workflow

CommandAction
just formatRuns formatters for all four languages
just lintRuns linters for all four languages
just testRuns test suites for all four languages
just installInstalls dependencies for all four languages

Summary of Key Rules

Top 10 Convention Rules
  1. Never edit generated files — look for AUTO-GENERATED FILE - DO NOT EDIT
  2. Use language-idiomatic naming — camelCase (TS), snake_case (Py), PascalCase (Go/C#)
  3. Prefix unused variables with _ in TypeScript (ESLint enforced)
  4. 100-character line limit in TS (Prettier) and Python (Ruff)
  5. Double quotes everywhere (TS Prettier, Python Ruff)
  6. 4-space indentation in TypeScript; spaces everywhere
  7. All public methods must be documented with language-standard format
  8. Error handling must not crash dispatchers — catch/recover in all languages
  9. API parity across all four languages — same names, same behavior, shared schemas
  10. errcheck is disabled in Go — explicit opt-out