2026-03-11 13:02:40 +00:00
---
title: Development Guide
description: How to build, test, lint, and contribute to go-session.
---
2026-02-20 15:01:55 +00:00
# Development Guide
## Prerequisites
2026-03-11 13:02:40 +00:00
- **Go 1.26 or later** -- the module requires Go 1.26 (`go.mod` ). The benchmark suite uses `b.Loop()` , introduced in Go 1.25.
- **`github.com/stretchr/testify` ** -- test-only dependency, fetched automatically by `go test` .
- **`vhs` ** (`github.com/charmbracelet/vhs` ) -- optional, required only for `RenderMP4` . Install with `go install github.com/charmbracelet/vhs@latest` .
- **`golangci-lint` ** -- optional, for running the full lint suite. Configuration is in `.golangci.yml` .
2026-02-20 15:01:55 +00:00
## Build and Test
```bash
# Run all tests
go test ./...
# Run a single test by name
go test -v -run TestParseTranscript_ToolCalls_Good
# Run with race detector
go test -race ./...
# Run benchmarks
go test -bench=. -benchmem ./...
# Vet the package
go vet ./...
2026-03-11 13:02:40 +00:00
# Format code
gofmt -w .
# Lint (requires golangci-lint)
golangci-lint run ./...
2026-02-20 15:01:55 +00:00
# Check test coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```
2026-03-11 13:02:40 +00:00
If this module is part of the Go workspace at `~/Code/go.work` , you can also use the `core` CLI:
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
```bash
core go test
core go qa # fmt + vet + lint + test
core go cov --open # coverage with HTML report
```
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
## Test Structure
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
All tests are in the `session` package (white-box). Test files are co-located with their corresponding source files:
| Source file | Test file | What it covers |
|-------------|-----------|---------------|
| `parser.go` | `parser_test.go` | `ParseTranscript` , `ParseTranscriptReader` , `ListSessions` , `ListSessionsSeq` , `FetchSession` , `extractToolInput` , `extractResultContent` , `truncate` , `shortID` , `formatDuration` , edge cases (malformed JSON, truncated lines, binary garbage, null bytes, 5 MiB lines) |
| `analytics.go` | `analytics_test.go` | `Analyse` (nil, empty, single tool, mixed tools with errors, latency calculations, token estimation), `FormatAnalytics` |
| `html.go` | `html_test.go` | `RenderHTML` (basic session, empty session, error events, XSS protection, invalid path, label-per-tool-type) |
| `video.go` | `video_test.go` | `generateTape` (basic, skips non-tool events, failed commands, long output truncation, Task/Edit/Write events, empty session, empty command), `extractCommand` , `RenderMP4` error path |
| `search.go` | `search_test.go` | `Search` and `SearchSeq` (empty directory, no matches, single/multiple matches, case-insensitive, output matching, skips non-tool events, ignores non-JSONL files, skips malformed sessions) |
| -- | `bench_test.go` | Performance benchmarks for parsing, listing, and searching |
2026-02-20 15:01:55 +00:00
### Test Naming Convention
2026-03-11 13:02:40 +00:00
Tests use a `_Good` , `_Bad` , `_Ugly` suffix pattern:
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
- **`_Good` **: happy path; valid inputs, expected successful output.
- **`_Bad` **: expected error conditions or graceful degradation (malformed input, missing optional data).
- **`_Ugly` **: panic-inducing or extreme edge cases (missing files, nil input, binary garbage, path errors).
2026-02-20 15:01:55 +00:00
### Test Helpers
2026-03-11 13:02:40 +00:00
`parser_test.go` defines helpers for building synthetic JSONL content without external fixtures:
2026-02-20 15:01:55 +00:00
```go
// Fixed epoch: 2026-02-20 10:00:00 UTC, offset by seconds
ts(offsetSec int) string
// Marshal arbitrary map to a single JSONL line
2026-03-11 13:02:40 +00:00
jsonlLine(m map[string]any) string
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
// Convenience builders for specific entry types
2026-02-20 15:01:55 +00:00
userTextEntry(timestamp, text string) string
assistantTextEntry(timestamp, text string) string
2026-03-11 13:02:40 +00:00
toolUseEntry(timestamp, toolName, toolID string, input map[string]any) string
toolResultEntry(timestamp, toolUseID string, content any, isError bool) string
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
// Write lines to a temp .jsonl file, return the file path
2026-02-20 15:01:55 +00:00
writeJSONL(t *testing.T, dir string, name string, lines ...string) string
```
2026-03-11 13:02:40 +00:00
All test output uses `t.TempDir()` , which Go cleans up automatically after each test.
### Benchmarks
The benchmark suite generates synthetic JSONL files with a realistic distribution of tool types (Bash, Read, Edit, Grep, Glob):
| Benchmark | File size | Tool pairs |
|-----------|-----------|------------|
| `BenchmarkParseTranscript` | ~2.2 MB | 5,000 |
| `BenchmarkParseTranscript_Large` | ~11 MB | 25,000 |
| `BenchmarkListSessions` | 20 files, 100 pairs each | -- |
| `BenchmarkSearch` | 10 files, 500 pairs each | -- |
Run with:
```bash
go test -bench=. -benchmem ./...
```
2026-02-20 15:01:55 +00:00
### Coverage Targets
2026-03-11 13:02:40 +00:00
The current statement coverage is 90.9%. New contributions should maintain or improve this figure. When adding a function, add tests covering at minimum:
2026-02-20 15:01:55 +00:00
- The success path.
- Nil or zero-value inputs where applicable.
- At least one error path.
## Coding Standards
### Language
2026-03-11 13:02:40 +00:00
UK English throughout all source code comments, documentation, and commit messages. Examples: `colour` , `organisation` , `licence` , `initialise` , `centre` .
2026-02-20 15:01:55 +00:00
### Formatting and Lint
2026-03-11 13:02:40 +00:00
Code must be formatted with `gofmt` . The project uses `golangci-lint` with the following linters enabled (see `.golangci.yml` ):
- `govet` , `errcheck` , `staticcheck` , `unused` , `gosimple`
- `ineffassign` , `typecheck` , `gocritic` , `gofmt`
Both `go vet ./...` and `golangci-lint run ./...` must be clean before committing.
2026-02-20 15:01:55 +00:00
### Types and Declarations
- Use explicit types on struct fields and function signatures.
- Avoid `interface{}` in public APIs; use typed parameters where possible.
- Handle all errors explicitly; do not use blank `_` for error returns in non-test code.
2026-03-26 11:14:35 +00:00
- Exported declarations must have Go doc comments beginning with the identifier name.
### Imports and Error Handling
- Do not import `errors` or `github.com/pkg/errors` in non-test Go files; use `coreerr.E(op, msg, err)` from `dappco.re/go/core/log` .
- Do not reintroduce legacy `forge.lthn.ai/...` module paths; use `dappco.re/go/core/...` imports.
### Test Naming
Test functions should follow `TestFunctionName_Context_Good/Bad/Ugly` .
The conventions test suite checks test naming, banned imports, and exported usage comments during `go test ./...` .
2026-02-20 15:01:55 +00:00
### File Headers
2026-03-11 13:02:40 +00:00
Source files should carry the SPDX licence identifier:
2026-02-20 15:01:55 +00:00
```go
// SPDX-Licence-Identifier: EUPL-1.2
package session
```
### Licence
2026-03-11 13:02:40 +00:00
EUPL-1.2. All new source files must include the SPDX header. By contributing, you agree that your contributions will be licensed under the European Union Public Licence 1.2.
2026-02-20 15:01:55 +00:00
## Commit Guidelines
Use conventional commits:
```
type(scope): description
```
Common types: `feat` , `fix` , `test` , `refactor` , `docs` , `chore` .
Examples:
```
2026-03-11 13:02:40 +00:00
feat(parser): add ParseTranscriptReader for streaming parse
2026-02-20 15:01:55 +00:00
fix(html): escape data-text attribute value
test(analytics): add latency calculation edge cases
2026-03-11 13:02:40 +00:00
docs(architecture): update scanner buffer size
2026-02-20 15:01:55 +00:00
```
All commits must include the co-author trailer:
```
Co-Authored-By: Virgil < virgil @lethean .io >
```
2026-03-11 13:02:40 +00:00
`go test ./...` must pass before committing.
2026-02-20 15:01:55 +00:00
## Adding a New Tool Type
1. Define an input struct in `parser.go` :
2026-03-11 13:02:40 +00:00
2026-02-20 15:01:55 +00:00
```go
type myToolInput struct {
SomeField string `json:"some_field"`
}
```
2. Add a `case "MyTool":` branch in `extractToolInput` that unmarshals the struct and returns a human-readable string.
2026-03-11 13:02:40 +00:00
3. Add a corresponding case in `html.go` 's input label logic (inside `RenderHTML` ) if the label should differ from the default `"Command"` . For example, if MyTool targets a URL, use `"Target"` .
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
4. Add a case in `video.go` 's `generateTape` switch if the tool should appear in VHS tape output.
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
5. Add tests in `parser_test.go` :
- A `TestExtractToolInput_MyTool_Good` test for `extractToolInput` .
- An integration test using `toolUseEntry` + `toolResultEntry` to exercise the full parse pipeline.
2026-02-20 15:01:55 +00:00
## Adding Analytics Fields
2026-03-11 13:02:40 +00:00
`analytics.go` is a pure computation layer with no I/O. To add a new metric:
2026-02-20 15:01:55 +00:00
2026-03-11 13:02:40 +00:00
1. Add the field to the `SessionAnalytics` struct.
2026-02-20 15:01:55 +00:00
2. Populate it in the `Analyse` function's event iteration loop.
3. Add a row to `FormatAnalytics` if it should appear in CLI output.
4. Add a test case in `analytics_test.go` .
## Module Path and Go Workspace
2026-03-22 01:37:11 +00:00
The module path is `dappco.re/go/core/session` . If this package is used within a Go workspace, add it with:
2026-03-11 13:02:40 +00:00
```bash
go work use ./go-session
go work sync
```