singlesource

package
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package singlesource is the Harbor Protocol single-source enforcement checker (CLAUDE.md §8, §13; RFC §5). It is the Phase 58 formalisation of the single-source discipline Phase 54 (D-072) laid the foundation for: the canonical Protocol packages are the ONLY definition sites, and a hardcoded Protocol method string / error code / wire-type redefinition anywhere else is a build-gating lint failure.

What is single-sourced, and where

  • Protocol method names — internal/protocol/methods. Every method wire string is a constant there; no method string literal appears anywhere else under internal/protocol/ (CLAUDE.md §8: "No hardcoded method strings elsewhere").
  • Protocol error codes — internal/protocol/errors. Every Code constant is declared there; no other package declares a protocol/errors.Code constant (CLAUDE.md §8: "Add new codes there and only there").
  • Protocol wire types — internal/protocol/types. Every canonical Protocol message struct is declared there; no other package re-declares one (CLAUDE.md §13: "Adding a third place to define Protocol message types" is rejection-on-sight).

Why a go/parser checker, not a golangci-lint analyzer or a script

The repo already proves the pattern: internal/planner/conformance/ importgraph_test.go is a go/parser AST walk that gates the §13 planner-does-not-import-runtime invariant with zero external-tool dependencies. Phase 58 reuses that shape — a custom golangci-lint analyzer would need a plugin build and a .golangci.yml entry (a new linter needs a PR rationale per CLAUDE.md §5), and a shell script could not parse Go reliably (a method string inside a comment or a struct-tag is not a violation; only a real string-literal expression is). go/parser sees the AST, so the checker is precise: it flags a BasicLit STRING whose unquoted value is a canonical method name, not a substring match. The checker is plain Go, runs as a `go test`, and is gated by CI + the preflight smoke exactly like the importgraph lint.

The checker is a reusable artifact (D-025)

ScanProtocolTree and its helpers are pure functions over a filesystem root — no package-level mutable state, safe to call concurrently. The Phase 58 test is the first consumer; a later phase (e.g. a `harbor lint` subcommand, or Phase 59's versioning discipline) can call the same checker without a second implementation.

Index

Constants

View Source
const (
	// KindMethodLiteral — a Protocol method wire string appears as a
	// string literal outside internal/protocol/methods.
	KindMethodLiteral = "method-literal"
	// KindErrorCode — a protocol/errors.Code constant is declared
	// outside internal/protocol/errors.
	KindErrorCode = "error-code"
	// KindWireType — a canonical Protocol message struct type is
	// declared outside internal/protocol/types.
	KindWireType = "wire-type"
)

The Kind* constants classify a Violation. They are stable strings so a test (or a future `harbor lint` subcommand) can branch on the kind.

Variables

View Source
var CanonicalMethods = map[string]struct{}{
	"start":                 {},
	"cancel":                {},
	"pause":                 {},
	"resume":                {},
	"redirect":              {},
	"inject_context":        {},
	"approve":               {},
	"reject":                {},
	"prioritize":            {},
	"user_message":          {},
	"events.subscribe":      {},
	"events.aggregate":      {},
	"search.query":          {},
	"search.sessions":       {},
	"search.tasks":          {},
	"search.events":         {},
	"search.artifacts":      {},
	"runtime.info":          {},
	"runtime.health":        {},
	"runtime.counters":      {},
	"runtime.drivers":       {},
	"metrics.snapshot":      {},
	"governance.posture":    {},
	"llm.posture":           {},
	"pause.list":            {},
	"topology.snapshot":     {},
	"artifacts.list":        {},
	"artifacts.put":         {},
	"artifacts.get_ref":     {},
	"artifacts.delete":      {},
	"memory.list":           {},
	"memory.get":            {},
	"memory.health":         {},
	"memory.strategy_trace": {},
	"memory.put":            {},
	"memory.delete":         {},

	"mcp.servers.list":               {},
	"mcp.servers.get":                {},
	"mcp.servers.resources":          {},
	"mcp.servers.prompts":            {},
	"mcp.servers.refresh_discovery":  {},
	"mcp.servers.probe":              {},
	"mcp.servers.health":             {},
	"mcp.servers.bindings.list":      {},
	"mcp.servers.policy":             {},
	"mcp.servers.refresh_binding":    {},
	"mcp.servers.revoke_binding":     {},
	"mcp.servers.set_raw_html_trust": {},

	"tools.list":                {},
	"tools.get":                 {},
	"tools.describe":            {},
	"tools.metrics":             {},
	"tools.content_stats":       {},
	"tools.set_approval_policy": {},
	"tools.revoke_oauth":        {},

	"tasks.list": {},
	"tasks.get":  {},

	"sessions.list":    {},
	"sessions.inspect": {},

	"runs.set_overrides": {},

	"auth.rotate_token": {},

	"flows.list":          {},
	"flows.describe":      {},
	"flows.runs.list":     {},
	"flows.runs.describe": {},
	"flows.run":           {},
	"flows.metrics":       {},

	"agents.list":        {},
	"agents.get":         {},
	"agents.tools":       {},
	"agents.memory":      {},
	"agents.governance":  {},
	"agents.skills":      {},
	"agents.permissions": {},
	"agents.metrics":     {},

	"agents.pause":      {},
	"agents.drain":      {},
	"agents.restart":    {},
	"agents.force_stop": {},
	"agents.deregister": {},
}

CanonicalMethods is the set of Protocol method wire strings that must only ever appear as constants in internal/protocol/methods. It is kept in lockstep with internal/protocol/methods by TestSingleSource_CanonicalMethodsInLockstep — the checker does NOT import the methods package (the checker must be runnable against a tree where methods/ itself is the thing under audit), so the set is duplicated here and the test pins the duplication.

View Source
var CanonicalWireTypes = map[string]string{}/* 214 elements not displayed */

CanonicalWireTypes maps each canonical Protocol message struct type name to the single package directory (relative to the protocol-tree root) that is allowed to declare it. A declaration of one of these type names in ANY other package is a KindWireType violation.

Almost every wire type lives in internal/protocol/types; the one exception is Error, the Protocol error wire type, which lives in internal/protocol/errors alongside the Code constants it carries (D-072 §1: "internal/protocol/errors/errors.go ... the Error wire type"). Single-sourcing means "exactly one home", not "all in the same directory" — the map records the home per type.

Version, Deprecation, and VersionHandshake are the Phase 59 (D-077) versioning-discipline wire types — all in internal/protocol/types alongside the ProtocolVersion pin.

Kept in lockstep with the canonical packages by TestSingleSource_CanonicalWireTypesInLockstep.

Functions

This section is empty.

Types

type Violation

type Violation struct {
	// File is the offending file, relative to the scanned root.
	File string
	// Line is the 1-based line number of the offending token.
	Line int
	// Kind classifies the breach — one of the Kind* constants.
	Kind string
	// Detail is a human-readable explanation naming the offending
	// identifier / literal and the canonical package it belongs in.
	Detail string
}

Violation is a single single-source breach found by a scan. It names the offending file (repo-relative), the 1-based line, the kind of breach, and a human-readable detail. A scan returns every Violation it finds in one pass so an operator sees the full extent of the drift, not just the first breach.

func ScanProtocolTree

func ScanProtocolTree(protocolRoot string) ([]Violation, error)

ScanProtocolTree walks the Go source tree rooted at protocolRoot (expected to be the internal/protocol directory) and returns every single-source Violation it finds. It parses .go files — including _test.go files, because a method string hardcoded in a test is the same drift as one hardcoded in production — with go/parser, so the check is precise: a method name inside a comment, a doc string, or a struct tag is NOT flagged; only a real string-literal expression, a real const declaration, or a real type declaration is.

protocolRoot may be absolute or relative; reported Violation.File paths are slash-separated and relative to protocolRoot either way.

The scan is exhaustive (it returns ALL violations, not the first) and deterministic (violations are sorted by file then line). It has no package-level mutable state and is safe for concurrent use (D-025).

A returned error means the walk itself failed (an unreadable file, an unparseable source file) — that is distinct from a Violation, which is a successful scan finding drift.

func (Violation) String

func (v Violation) String() string

String renders a Violation as a single `file:line: kind: detail` line, the shape a test failure message joins.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL