ts/docs/development.md
Snider 9466bd7bc5 docs: add human-friendly documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:02:40 +00:00

7.1 KiB

title description
Development How to build, test, and contribute to CoreTS.

Development

Prerequisites

  • Go 1.26+ (uses Go workspaces)
  • Deno 2.x (required for integration tests)
  • protoc + Go/gRPC plugins (only if regenerating protobuf stubs)

This module is part of a Go workspace at ~/Code/go.work. After cloning, ensure the workspace includes it:

go work use ./core/ts

Building

CoreTS is a library package with no standalone binary. It compiles as part of applications that import it:

go build ./...

Running Tests

Unit Tests

Unit tests cover the Go side without requiring Deno:

core go test
# or
go test ./...

Tests use the _Good, _Bad, _Ugly suffix convention:

  • _Good -- happy path
  • _Bad -- expected error conditions
  • _Ugly -- panics and edge cases

Integration Tests

Integration tests require a working Deno installation and are gated behind the integration build tag:

go test -tags integration -timeout 60s ./...

These tests boot the full CoreTS stack (Go gRPC server + Deno sidecar + Workers) and verify end-to-end communication. They are organised in tiers:

Tier Test What it proves
1 TestIntegration_FullBoot_Good Go gRPC server starts, Deno sidecar launches, store round-trip works
2 TestIntegration_Tier2_Bidirectional_Good Go can call Deno (LoadModule/UnloadModule/ModuleStatus), bidirectional communication
3 TestIntegration_Tier3_WorkerIsolation_Good Module Workers can call back to Go via the I/O bridge (store write from inside a Worker)
4 TestIntegration_Tier4_MarketplaceInstall_Good Full marketplace flow: install from Git, load module, verify I/O bridge, unload, remove

If Deno is not installed, integration tests are automatically skipped.

Single Test

core go test --run TestCheckPath_Good_Allowed
# or
go test -run TestCheckPath_Good_Allowed ./...

Test Coverage

core go cov
core go cov --open    # Opens HTML report

Code Quality

core go fmt     # Format
core go lint    # Lint
core go vet     # Vet
core go qa      # All of the above + tests
core go qa full # + race detector, vulnerability scan

Regenerating Protobuf Stubs

If you modify proto/coredeno.proto, regenerate the Go stubs:

protoc --go_out=. --go-grpc_out=. proto/coredeno.proto

Ensure you have the protoc-gen-go and protoc-gen-go-grpc plugins installed:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

The Deno side loads the .proto file dynamically at runtime via @grpc/proto-loader, so no TypeScript code generation is needed.

Project Structure

forge.lthn.ai/core/ts/
├── coredeno.go          # Options, Permissions, Sidecar types
├── coredeno_test.go     # Unit tests for options and sidecar creation
├── lifecycle.go         # Sidecar Start/Stop/IsRunning
├── lifecycle_test.go    # Unit tests for process lifecycle
├── listener.go          # ListenGRPC -- Unix socket gRPC server
├── listener_test.go     # Unit tests for gRPC listener
├── server.go            # CoreService gRPC implementation
├── server_test.go       # Unit tests for gRPC handlers + permissions
├── denoclient.go        # DenoClient JSON-RPC client
├── permissions.go       # CheckPath/CheckNet/CheckRun helpers
├── permissions_test.go  # Unit tests for permission checks
├── service.go           # Framework Service integration
├── service_test.go      # Unit tests for service lifecycle
├── integration_test.go  # End-to-end tests (build tag: integration)
├── go.mod
├── go.sum
├── proto/
│   ├── coredeno.proto       # Service + message definitions
│   ├── coredeno.pb.go       # Generated protobuf code
│   └── coredeno_grpc.pb.go  # Generated gRPC stubs
└── runtime/
    ├── main.ts              # Deno entry point
    ├── client.ts            # CoreService gRPC client (Deno calls Go)
    ├── server.ts            # DenoService JSON-RPC server (Go calls Deno)
    ├── modules.ts           # Module registry + Worker isolation
    ├── worker-entry.ts      # Worker bootstrap script
    ├── polyfill.ts          # Deno 2.x http2/grpc-js patches
    ├── deno.json            # Deno configuration + npm imports
    ├── deno.lock            # Lock file
    └── testdata/
        └── test-module.ts   # Test fixture for integration tests

Writing a TypeScript Module

A module is a TypeScript file that exports an init function:

export async function init(core: any) {
    // Use the I/O bridge to interact with Go-managed resources
    await core.storeSet("my-module", "status", "running");

    const data = await core.fileRead("./data/config.json");
    console.log("Config loaded:", data);
}

The core object provides:

Method Description
core.storeGet(group, key) Read from the key-value store
core.storeSet(group, key, value) Write to the key-value store
core.fileRead(path) Read a file (permission-gated)
core.fileWrite(path, content) Write a file (permission-gated)
core.processStart(command, args) Start a subprocess (permission-gated)
core.processStop(processId) Stop a subprocess

All operations are relayed through the Go gRPC server and checked against the module's declared permissions.

Adding a New gRPC Method

  1. Add the RPC and message definitions to proto/coredeno.proto
  2. Regenerate Go stubs: protoc --go_out=. --go-grpc_out=. proto/coredeno.proto
  3. Implement the handler in server.go with appropriate permission checks
  4. Add the method to runtime/client.ts (CoreService calls) or runtime/server.ts (DenoService calls)
  5. If the method should be available to Workers, add it to runtime/worker-entry.ts and runtime/modules.ts (dispatchRPC)
  6. Write unit tests (server_test.go) and integration tests (integration_test.go)

Coding Standards

  • UK English in documentation and user-facing strings (colour, organisation, centre)
  • Strict typing -- all Go function parameters and return types must be declared
  • Test naming -- use _Good, _Bad, _Ugly suffixes
  • Error context -- wrap errors with the subsystem prefix: fmt.Errorf("coredeno: <context>: %w", err)
  • Thread safety -- use sync.RWMutex for shared state; the Sidecar and DenoClient are both thread-safe
  • Secure by default -- empty permission lists deny all access; reserved store namespaces are blocked

Dependency Graph

forge.lthn.ai/core/ts
├── forge.lthn.ai/core/go        (DI container, ServiceRuntime)
├── forge.lthn.ai/core/go-io     (Sandboxed Medium, MockMedium, Store)
├── forge.lthn.ai/core/go-scm    (Manifest loading, Marketplace installer)
├── google.golang.org/grpc       (gRPC server + client)
└── google.golang.org/protobuf   (Protocol buffer runtime)

CoreTS has no circular dependencies. It depends on the core framework but the framework does not depend on it.