go-session/docs/development.md
Snider 3e00791c2f docs: graduate TODO/FINDINGS into production documentation
Replace internal task tracking (TODO.md, FINDINGS.md) with structured
documentation in docs/. Trim CLAUDE.md to agent instructions only.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-02-20 15:01:55 +00:00

5.1 KiB

Development Guide

Prerequisites

  • Go 1.25 or later (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.

Build and Test

# 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 ./...

# Check test coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

The benchmark suite generates synthetic JSONL files of varying sizes:

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

Test Patterns

All tests are in the session package (white-box). Test files are co-located with source files.

Test Naming Convention

Tests use a _Good, _Bad, _Ugly suffix:

  • _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, path errors).

Test Helpers

parser_test.go defines a set of helpers for building synthetic JSONL content:

// Fixed epoch: 2026-02-20 10:00:00 UTC, offset by seconds
ts(offsetSec int) string

// Marshal arbitrary map to a single JSONL line
jsonlLine(m map[string]interface{}) string

// Convenience builders
userTextEntry(timestamp, text string) string
assistantTextEntry(timestamp, text string) string
toolUseEntry(timestamp, toolName, toolID string, input map[string]interface{}) string
toolResultEntry(timestamp, toolUseID string, content interface{}, isError bool) string

// Write lines to a temp .jsonl file, return path
writeJSONL(t *testing.T, dir string, name string, lines ...string) string

Use t.TempDir() for all test output; Go cleans it up automatically after each test.

Coverage Targets

The current statement coverage is 90.9%. New contributions should maintain or improve this figure. When adding a function, add corresponding tests covering at minimum:

  • The success path.
  • Nil or zero-value inputs where applicable.
  • At least one error path.

Coding Standards

Language

UK English throughout all source code comments, documentation, and commit messages. Examples: colour, organisation, licence, initialise.

Formatting and Lint

Code must be formatted with gofmt (or goimports). go vet ./... must be clean before committing.

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.

File Headers

Source files that are part of the main package should carry the SPDX licence identifier:

// SPDX-Licence-Identifier: EUPL-1.2
package session

Licence

EUPL-1.2. All new source files must include the SPDX header.

Commit Guidelines

Use conventional commits:

type(scope): description

Common types: feat, fix, test, refactor, docs, chore.

Examples:

feat(parser): add ParseStats orphan detection
fix(html): escape data-text attribute value
test(analytics): add latency calculation edge cases

All commits must include the co-author trailer:

Co-Authored-By: Virgil <virgil@lethean.io>

go test ./... must pass before committing. The repository does not use a CI gate at present, so this is a manual requirement.

Adding a New Tool Type

  1. Define an input struct in parser.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.

  3. Add a corresponding case in html.go's input label logic if the label should differ from the default "Command".

  4. Add a case in video.go's generateTape switch if the tool should appear in the VHS tape.

  5. Add tests in parser_test.go for extractToolInput covering the new tool name, and an integration test using toolUseEntry + toolResultEntry to exercise the full parse path.

Adding Analytics Fields

analytics.go is a pure computation layer. To add a new metric:

  1. Add the field to SessionAnalytics.
  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

The module path is forge.lthn.ai/core/go-session. If this package is used within the Go workspace at forge.lthn.ai/core/go, add it to go.work with go work use ./go-session and run go work sync.