core

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Index

Constants

View Source
const (
	CriterionStatusPass = "pass"
	CriterionStatusFail = "fail"
)

CriterionStatusPass / Fail are the only valid statuses; the verify command's declared flag enum rejects anything else before this layer is reached.

View Source
const (
	HistorySourceApproval = iota
	HistorySourceDecision
	HistorySourceMidReq
	HistorySourceVerify
	HistorySourceCompletion
	HistorySourceCriterion
	HistorySourceEscalation
	HistorySourceSubmission
	HistorySourceACP
)

History source ranks. The values fix the tie-break order for events sharing a timestamp; they are an internal ordering key, not a public contract.

View Source
const (
	ReviewApprove      = "approve"
	ReviewReject       = "reject"
	ReviewNeedsChanges = "needs-changes"
)

Review verdicts. Anything else — including an unedited placeholder — fails the review parse and is never treated as approve (spec 09 R5, fail closed).

View Source
const EscalationDefaultMaxVerifyFails = 3

EscalationDefaultMaxVerifyFails is the ratchet threshold when config leaves escalation.max_verify_fails unset.

View Source
const HelpSchemaVersion = 1

HelpSchemaVersion versions the machine-readable help palette (`help --json`). Consumers (MCP, role prompts, external tools) pin against it and can detect a shape change; bump it whenever the Command/Flag JSON contract changes (spec 03 R4, pairs with the state-schema discipline of spec 02).

View Source
const ProgramSchemaVersion = 1

ProgramSchemaVersion versions the program.json shape, following the same forward-migration discipline as state.json (spec 02). Bump it when the shape changes and add a migration in LoadProgram.

View Source
const StateSchemaVersion = 2
View Source
const SubmitDefaultTimeoutSecs = 120

SubmitDefaultTimeoutSecs bounds an operator submit command when the config leaves submit.timeout_seconds unset.

View Source
const TemplateVersion = 1

TemplateVersion is the current version stamp for specd-managed scaffold assets. It rides in every managed-region marker so `init --refresh` can detect a region written by an older binary. Bump it whenever a role/steering template changes.

View Source
const UnknownHead = "unknown"

UnknownHead is the sentinel gitHead writes when HEAD cannot be resolved (commitless repo, no git). Evidence carrying it is not pinned to a commit.

Variables

View Source
var Clock = func() time.Time { return time.Now().UTC() }

Clock is the injectable time source for record timestamps. Production uses wall-clock UTC; tests swap it for determinism. All record timestamps flow through here — never call time.Now directly in a record path.

View Source
var Commands = []Command{
	{
		Name:          "help",
		Usage:         "specd help [command] [--json]",
		Description:   "Show command help.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd help", "specd help --json"},
		Flags: []Flag{
			{Name: "json", Type: "bool", Description: "Emit machine-readable help."},
		},
	},
	{
		Name:          "version",
		Usage:         "specd version [--json]",
		Description:   "Print build version metadata.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd version", "specd version --json"},
		Flags:         []Flag{{Name: "json", Type: "bool", Description: "Emit machine-readable JSON."}},
	},
	{
		Name:          "init",
		Usage:         "specd init [--agent=<name>] [--repair|--refresh] [--dry-run]",
		Description:   "Initialize or re-sync specd project state and managed assets.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd init", "specd init --repair --dry-run", "specd init --refresh"},
		Flags: []Flag{
			{Name: "agent", TakesValue: true, Type: "string", Description: "Select agent harness."},
			{Name: "repair", Type: "bool", Description: "Restore drifted managed regions from the current templates."},
			{Name: "refresh", Type: "bool", Description: "Update managed regions to the current binary's template version."},
			{Name: "dry-run", Type: "bool", Description: "Print the managed-region changes and write nothing."},
		},
	},
	{
		Name:          "new",
		Usage:         "specd new <name> [--agent=<name>]",
		Description:   "Create a new spec workspace.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd new payments", "specd new payments --agent=codex"},
		Flags: []Flag{
			{Name: "agent", TakesValue: true, Type: "string", Description: "Select agent harness."},
		},
	},
	{
		Name:          "approve",
		Usage:         "specd approve <spec> <gate>",
		Description:   "Record human approval for a lifecycle gate.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd approve payments requirements", "specd approve payments design"},
	},
	{
		Name:          "midreq",
		Usage:         "specd midreq <spec> --text <change> [--scope <scope>]",
		Description:   "Capture a scoped mid-stream requirement change.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd midreq payments --text 'add refund path' --scope requirements"},
		Flags: []Flag{
			{Name: "text", TakesValue: true, Type: "string", Description: "Change description (required)."},
			{Name: "scope", TakesValue: true, Type: "string", Description: "Optional scope label."},
		},
	},
	{
		Name:          "decision",
		Usage:         "specd decision <spec> --text <rationale> [--scope <scope>]",
		Description:   "Record an explicit human decision.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd decision payments --text 'defer webhooks' --scope design"},
		Flags: []Flag{
			{Name: "text", TakesValue: true, Type: "string", Description: "Decision rationale (required)."},
			{Name: "scope", TakesValue: true, Type: "string", Description: "Optional scope label."},
		},
	},
	{
		Name:          "next",
		Usage:         "specd next <slug> [--json | --waves | --dispatch]",
		Description:   "Select the next eligible task or wave.",
		AllowedPhases: postRequirementsPhases(),
		SpecSlugArg:   argAt(0),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd next payments", "specd next payments --json"},
		Flags: []Flag{
			{Name: "waves", Type: "bool", Description: "Show all wave groups as JSON."},
			{Name: "dispatch", Type: "bool", Description: "Emit the context manifest for the first frontier task."},
			{Name: "json", Type: "bool", Description: "Emit machine-readable frontier list."},
		},
	},
	{
		Name:          "status",
		Usage:         "specd status [spec] [--json] | specd status --program",
		Description:   "Report current spec and task state, or the cross-spec program view.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd status payments", "specd status payments --json", "specd status --program"},
		Flags: []Flag{
			{Name: "json", Type: "bool", Description: "Emit machine-readable status."},
			{Name: "program", Type: "bool", Description: "Show the cross-spec program view: specs, links, phases, and frontier."},
		},
	},
	{
		Name:          "task",
		Usage:         "specd task <id> [--override --reason <text>] | specd task complete <spec> <id>",
		Description:   "Show task details, clear an escalated task with a human override, or mark a task complete (requires passing evidence).",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd task T3 --json", "specd task T3 --override --reason 'flaky infra, verified manually'", "specd task complete payments T3"},
		Flags: []Flag{
			{Name: "json", Type: "bool", Description: "Emit machine-readable task row."},
			{Name: "override", Type: "bool", Description: "Clear an escalated task (resets the verify-failure ratchet; does not complete it). Requires --reason."},
			{Name: "reason", TakesValue: true, Type: "string", Description: "Human justification for --override (required, non-empty)."},
			{Name: "tokens", TakesValue: true, Type: "string", Description: "Optional worker-reported token count, stored verbatim (task complete)."},
			{Name: "cost", TakesValue: true, Type: "string", Description: "Optional worker-reported cost as a decimal string, stored verbatim (task complete)."},
			{Name: "duration-ms", TakesValue: true, Type: "string", Description: "Optional worker-reported wall-clock milliseconds, stored verbatim (task complete)."},
		},
	},
	{
		Name:          "check",
		Usage:         "specd check <spec> [--security] [--json]",
		Description:   "Run the validation gate registry against a spec.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd check payments", "specd check payments --security --json"},
		Flags: []Flag{
			{Name: "security", Type: "bool", Description: "Run opt-in security gates."},
			{Name: "schema", Type: "bool", Description: "Validate state.json schema."},
			{Name: "schema-only", Type: "bool", Description: "Validate only state.json schema."},
			{Name: "json", Type: "bool", Description: "Emit machine-readable findings."},
		},
	},
	{
		Name:          "verify",
		Usage:         "specd verify <slug> <task-id> [--revert-on-fail] [--sandbox] [--sandbox-binary=<path>] | specd verify <slug> --criterion <r>.<n> --status pass|fail --evidence <text>",
		Description:   "Run and record task verification (task mode), or record a per-acceptance-criterion evidence record (--criterion mode).",
		AllowedPhases: postRequirementsPhases(),
		SpecSlugArg:   argAt(0),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd verify payments T3", "specd verify payments T3 --revert-on-fail", "specd verify payments --criterion 1.2 --status pass --evidence 'covered by T3 integration test'"},
		Flags: []Flag{
			{Name: "revert-on-fail", Type: "bool", Description: "Restore working tree on verify failure."},
			{Name: "sandbox", Type: "bool", Description: "Run the verify line inside a bwrap sandbox (fail-closed if the binary is absent)."},
			{Name: "sandbox-binary", TakesValue: true, Type: "string", Description: "Path to sandbox binary (overrides auto-detect)."},
			{Name: "criterion", TakesValue: true, Type: "string", Description: "Record evidence for acceptance criterion <r>.<n> instead of running a task verify."},
			{Name: "status", TakesValue: true, Type: "string", Enum: []string{"pass", "fail"}, Description: "Criterion verdict (with --criterion): pass|fail."},
			{Name: "evidence", TakesValue: true, Type: "string", Description: "Evidence text or path backing the criterion verdict (with --criterion)."},
			{Name: "tokens", TakesValue: true, Type: "string", Description: "Optional worker-reported token count, stored verbatim."},
			{Name: "cost", TakesValue: true, Type: "string", Description: "Optional worker-reported cost as a decimal string, stored verbatim."},
			{Name: "duration-ms", TakesValue: true, Type: "string", Description: "Optional worker-reported wall-clock milliseconds, stored verbatim."},
		},
	},
	{
		Name:          "context",
		Usage:         "specd context <slug> <task-id> [--json|--hud]",
		Description:   "Build the bounded context manifest for a task.",
		AllowedPhases: postRequirementsPhases(),
		SpecSlugArg:   argAt(0),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd context payments T3", "specd context payments T3 --hud"},
		Flags: []Flag{
			{Name: "json", Type: "bool", Description: "Emit machine-readable context."},
			{Name: "hud", Type: "bool", Description: "Render the operator HUD (files, bytes, tokens, mode)."},
		},
	},
	{
		Name:          "memory",
		Usage:         "specd memory <slug> <add|promote> [flags]",
		Description:   "Append or promote steering-memory patterns (learning flywheel).",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd memory payments add --key 'atomic writes' --pattern 'use AtomicWrite'"},
		Flags: []Flag{
			{Name: "key", TakesValue: true, Type: "string", Description: "Pattern key (H2 heading)."},
			{Name: "pattern", TakesValue: true, Type: "string", Description: "One-line pattern statement (add)."},
			{Name: "body", TakesValue: true, Type: "string", Description: "Detail of the pattern (add)."},
			{Name: "source", TakesValue: true, Type: "string", Description: "Where the pattern came from (add)."},
			{Name: "criticality", TakesValue: true, Type: "string", Enum: []string{"minor", "important", "critical"}, Description: "minor|important|critical (add)."},
			{Name: "related", TakesValue: true, Type: "string", Description: "Comma-separated related keys → wikilinks (add)."},
			{Name: "force", Type: "bool", Description: "Promote past the threshold (promote)."},
		},
	},
	{
		Name:          "mcp",
		Usage:         "specd mcp | specd mcp --config <host> [--root <path>] [--spec <slug>]",
		Description:   "Serve the MCP integration surface over stdio, or print a host config snippet.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd mcp", "specd mcp --config claude-code --spec demo"},
		Flags: []Flag{
			{Name: "config", TakesValue: true, Type: "string", Description: "Print a paste-ready MCP config snippet for a host (e.g. claude-code)."},
			{Name: "root", TakesValue: true, Type: "string", Description: "Pin the server working directory in the snippet."},
			{Name: "spec", TakesValue: true, Type: "string", Description: "Pin the active spec in the snippet."},
		},
	},
	{
		Name:          "handshake",
		Usage:         "specd handshake bootstrap [--json] [--expect-palette-digest <d>] [--expect-config-digest <d>]",
		Description:   "Emit bootstrap handshake material, including palette and config digests.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd handshake bootstrap", "specd handshake bootstrap --json"},
		Flags: []Flag{
			{Name: "json", Type: "bool", Description: "Emit machine-readable handshake."},
			{Name: "expect-palette-digest", TakesValue: true, Type: "string", Description: "Fail (exit 1) if the command-palette digest differs."},
			{Name: "expect-config-digest", TakesValue: true, Type: "string", Description: "Fail (exit 1) if the effective-config digest differs."},
		},
	},
	{
		Name:          "brain",
		Usage:         "specd brain <start|step|run|status|cancel|resume> <spec> [--authority]",
		Description:   "Run the opt-in deterministic orchestration controller.",
		AllowedPhases: postRequirementsPhases(),
		SpecSlugArg:   argAt(1),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd brain start payments --authority", "specd brain status payments", "specd brain resume payments", "specd brain cancel payments"},
		Flags: []Flag{
			{Name: "authority", Type: "bool", Description: "Grant dispatch authority (fail-closed by default)."},
		},
	},
	{
		Name:          "report",
		Usage:         "specd report <spec> [--pr|--metrics|--json|--history|--format prometheus]",
		Description:   "Render evidence-backed status, PR, history, and metrics reports.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd report payments --pr", "specd report payments --metrics", "specd report payments --history", "specd report payments --format prometheus"},
		Flags: []Flag{
			{Name: "pr", Type: "bool", Description: "Emit PR-oriented report."},
			{Name: "metrics", Type: "bool", Description: "Emit metrics summary."},
			{Name: "json", Type: "bool", Description: "Emit machine-readable report (JSON Lines with --history)."},
			{Name: "history", Type: "bool", Description: "Replay the spec's audit trail from existing records in timestamp order."},
			{Name: "format", TakesValue: true, Type: "string", Enum: []string{"prometheus"}, Description: "Alternate output format; prometheus emits textfile-collector metrics."},
		},
	},
	{
		Name:          "link",
		Usage:         "specd link <from-slug> <to-slug>",
		Description:   "Record that one spec depends on another (cross-spec ordering).",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd link api auth"},
	},
	{
		Name:          "unlink",
		Usage:         "specd unlink <from-slug> <to-slug>",
		Description:   "Remove a cross-spec dependency link.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd unlink api auth"},
	},
	{
		Name:          "review",
		Usage:         "specd review <spec> [--force]",
		Description:   "Scaffold the review report the auditor fills before completion.",
		AllowedPhases: postExecutionPhases(),
		SpecSlugArg:   argAt(0),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd review payments", "specd review payments --force"},
		Flags: []Flag{
			{Name: "force", Type: "bool", Description: "Overwrite an existing report for the current git HEAD."},
		},
	},
	{
		Name:          "submit",
		Usage:         "specd submit <spec> [--resubmit]",
		Description:   "Run every gate, then stream the PR summary to the operator-configured submit command.",
		AllowedPhases: postExecutionPhases(),
		SpecSlugArg:   argAt(0),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd submit payments", "specd submit payments --resubmit"},
		Flags: []Flag{
			{Name: "resubmit", Type: "bool", Description: "Allow resubmitting a spec already submitted at the current git HEAD."},
		},
	},
	{
		Name:          "triage",
		Usage:         "specd triage <spec>",
		Description:   "Run the opt-in extended-loop triage tier.",
		AllowedPhases: anyPhase(),
		ExitCodes:     stdCodes(),
		Examples:      []string{"specd triage payments"},
		Deferred:      true,
	},
}

Commands is the stable top-level command palette.

View Source
var DefaultConfig = Config{
	Version: "1",
	Agent:   "codex",
	Gates: GatesConfig{
		Verify: "error",
	},
	Context: ContextConfig{
		MaxTokens: 12000,
	},
	Orchestration: OrchestrationConfig{
		Enabled: false,
		Model:   "",
	},
	Security: SecurityConfig{
		Secrets:   "error",
		Injection: "warn",
		Slopsquat: "warn",
	},
	Escalation: EscalationConfig{
		MaxVerifyFails: EscalationDefaultMaxVerifyFails,
	},
	PromotionThreshold: 3,
}
View Source
var ErrRevisionConflict = errors.New("state revision conflict")
View Source
var SecuritySeverities = []string{"off", "warn", "error"}

SecuritySeverities enumerates the valid per-scanner severities.

Functions

func AlreadySubmittedAt added in v0.3.0

func AlreadySubmittedAt(records []SubmissionRecord, head string) bool

AlreadySubmittedAt reports whether the ledger holds a successful submission (exit 0) pinned to head. It is the idempotence guard behind the --resubmit requirement (spec 08 R5): a double-fire from orchestration at the same HEAD is refused unless the operator explicitly opts back in.

func AppendCriterion added in v0.3.0

func AppendCriterion(path string, rec CriterionRecord) error

AppendCriterion stamps and appends a criterion record. It fills the timestamp (Clock) and actor here so callers only supply the domain fields; the caller is responsible for holding the per-spec lock (see WithSpecLock) and for resolving gitHead.

func AppendEvidence added in v0.3.0

func AppendEvidence(path string, record EvidenceRecord) error

func AppendFile

func AppendFile(path, data string) error

AppendFile appends data to a non-secret artifact and fsyncs before returning.

func AppendOverride added in v0.3.0

func AppendOverride(path string, record OverrideRecord) error

AppendOverride appends one override record. An empty reason is rejected: a clearance with no stated reason is not a reasoned override (R3).

func AppendSubmission added in v0.3.0

func AppendSubmission(path string, rec SubmissionRecord) error

AppendSubmission stamps and appends a submission record. It fills the timestamp (Clock) and actor here; the caller resolves gitHead and holds the per-spec lock (see WithSpecLock).

func AtomicWrite

func AtomicWrite(path, data string) error

AtomicWrite writes data via a temp file in the target directory, fsyncs it, chmods to the non-secret artifact mode, then renames over the target.

func CanAdvanceStatus added in v0.3.0

func CanAdvanceStatus(from, to Status) bool

func CommandNames added in v0.3.0

func CommandNames() []string

CommandNames returns command names in help order.

func CommitLink(remote, head string) string

func CompleteTask

func CompleteTask(rawTasks []byte, taskID string, records map[string]EvidenceRecord) ([]byte, error)

func ConfigDigest added in v0.3.0

func ConfigDigest(config Config) string

ConfigDigest is the SHA-256 of the effective config.

func ConsecutiveVerifyFails added in v0.3.0

func ConsecutiveVerifyFails(evidence []EvidenceRecord, overrides []OverrideRecord, taskID string) int

ConsecutiveVerifyFails counts the trailing run of failing verify attempts for taskID over a merged timeline of evidence and overrides. A passing verify or an override resets the count to zero. Pure over its inputs; deterministic for identical logs (R6).

func CountSpecsWithBlock added in v0.3.0

func CountSpecsWithBlock(root, key string) int

CountSpecsWithBlock counts specs whose memory.md contains a `## <key>` block. The promotion threshold is a pure count of on-disk state — no LLM (RM.4).

func CriteriaPath added in v0.3.0

func CriteriaPath(root, slug string) string

CriteriaPath is the per-spec append-only criterion evidence ledger. It is kept separate from evidence.jsonl so the task-verify loader (last-write-wins per task) is untouched and the two evidence types stay physically distinct.

func CurrentPassing added in v0.3.0

func CurrentPassing(records []CriterionRecord, since time.Time) map[string]bool

CurrentPassing returns the set of criterion ids whose latest record is a pass recorded strictly after `since` (the last requirements approval). Re-approving requirements moves `since` forward and invalidates stale attestations by construction — no mutation of records needed (spec 04 R5/R6, "current").

A zero `since` means requirements were never approved; nothing counts.

func DedupRolePrompts added in v0.3.0

func DedupRolePrompts(prompts []string) []string

func EscalatedCounts added in v0.3.0

func EscalatedCounts(evidence []EvidenceRecord, overrides []OverrideRecord, tasks []TaskRow, maxFails int) map[string]int

EscalatedCounts returns, for each task that is currently escalated, its consecutive verify-fail count. Tasks below the threshold (or all tasks when the ratchet is disabled) are absent from the map. Pure over its inputs.

func EvidencePath added in v0.3.0

func EvidencePath(root, slug string) string

func ExtractMemBlock added in v0.3.0

func ExtractMemBlock(text, key string) string

ExtractMemBlock returns the `## <key>` block from text, from the heading up to (excluding) the next H2 heading, trailing whitespace trimmed. Returns "" when the key is absent. Pure function.

func FindRoot added in v0.3.0

func FindRoot(start string) (string, error)

func ForbiddenTool added in v0.3.0

func ForbiddenTool(name string) bool

func HasPassingEvidence added in v0.3.0

func HasPassingEvidence(records map[string]EvidenceRecord, taskID string) bool

func HeadPinned added in v0.3.0

func HeadPinned(gitHead string) bool

HeadPinned reports whether an evidence record's git_head names a real commit. Empty (pre-W3 records) and the "unknown" sentinel both fail: an evidence record that cannot be pinned to a commit does not count toward completion.

func IsEscalated added in v0.3.0

func IsEscalated(consecutiveFails, maxFails int) bool

IsEscalated reports whether a task with the given consecutive-fail count is escalated under the ratchet threshold. maxFails <= 0 disables the ratchet: no task is ever escalated (the escape hatch, R5).

func ListSpecs

func ListSpecs(root string) []string

ListSpecs enumerates spec slugs under .specd/specs/, sorted. Missing dir yields an empty list, not an error.

func LoadConfig

func LoadConfig(paths ConfigPaths, env map[string]string) (Config, []Diagnostic)

LoadConfig applies global YAML, project YAML, then environment overrides. The function is deterministic for explicit paths and env input.

func LoadEvidence added in v0.3.0

func LoadEvidence(path string) (map[string]EvidenceRecord, error)

func MCPConfigSnippet added in v0.3.0

func MCPConfigSnippet(host, root, spec string) (string, error)

MCPConfigSnippet returns a ready-to-paste MCP server configuration wiring `specd mcp` for the named host. Optional root pins the server's working directory; optional spec pins the active spec via env. An unknown host is an error naming the known hosts, which the caller maps to a fail-closed exit 2.

The snippet is built from a typed structure and marshaled, so it is always valid JSON with stable (sorted) key order — golden-testable and never a hand-concatenated string that can drift into invalid JSON.

func MCPHosts added in v0.3.0

func MCPHosts() []string

MCPHosts is the set of agent hosts specd can emit a paste-ready MCP config snippet for. `claude-code` is the minimum (spec 11 R1); the list is designed to grow — add a case in MCPConfigSnippet and an entry here.

func MergeAgents added in v0.3.0

func MergeAgents(existing, generated string) string

func OverridePath added in v0.3.0

func OverridePath(root, slug string) string

OverridePath is the per-spec append-only override ledger.

func PRSummary

func PRSummary(model ReportModel) string

func PaletteDigest added in v0.3.0

func PaletteDigest() string

PaletteDigest is the SHA-256 of the canonical `help --json` payload (spec 03). It is stable across runs (Go marshals struct fields in declaration order and map keys sorted) and changes when a verb or flag is added — exactly the drift an agent's --expect-palette-digest guard catches.

func ProgramPath

func ProgramPath(root string) string

ProgramPath is the program-level link store.

func ReadOrNull

func ReadOrNull(path string) *string

ReadOrNull returns the file contents, or nil when path does not exist.

func RenderHistory added in v0.3.0

func RenderHistory(slug string, events []HistoryEvent) string

RenderHistory prints the replay as one aligned line per event: timestamp, actor, event, reference. Empty fields render as "-" so columns stay stable.

func RenderHistoryJSON added in v0.3.0

func RenderHistoryJSON(events []HistoryEvent) (string, error)

RenderHistoryJSON emits one JSON object per line (JSON Lines), the same events as RenderHistory in the same order (spec 13 R6).

func RenderMemBlock added in v0.3.0

func RenderMemBlock(f MemFields) string

RenderMemBlock renders a byte-stable `## <key>` block. Output starts at the heading and ends with a trailing newline; callers prepend a blank line to separate appended blocks. Pure function of its input.

func RenderMetrics added in v0.3.0

func RenderMetrics(model ReportModel) string

func RenderPRSummary added in v0.3.0

func RenderPRSummary(model ReportModel) string

func RenderPrometheus added in v0.3.0

func RenderPrometheus(m PrometheusMetrics) string

RenderPrometheus emits node_exporter textfile-collector-compatible output: each metric family preceded by its HELP and TYPE lines, every series labelled with the spec slug, values in a deterministic order so repeated runs are byte-identical and no series is duplicated (R4, R5).

func RenderPromotion added in v0.3.0

func RenderPromotion(block, slug string, count int, date string) string

RenderPromotion renders the block plus a deterministic provenance line for the steering store. date is pre-formatted (UTC) by the caller so output is byte-deterministic under an injected clock (RM.7). Pure function.

func RenderReviewScaffold added in v0.3.0

func RenderReviewScaffold(slug, head string, tasks []TaskRow) string

RenderReviewScaffold builds the review report from the embedded template, substituting the spec slug, the git HEAD under review, and a per-task section (id, files, acceptance) so the reviewer sees exactly what to audit (R1).

func RenderStatus added in v0.3.0

func RenderStatus(model ReportModel) string

func RenderTelemetry added in v0.3.0

func RenderTelemetry(slug string, report TelemetryReport) string

RenderTelemetry formats a telemetry report for `report --metrics`, in the same Prometheus-flavored style as the task-count metrics. Tasks without telemetry are emitted as an explicit gauge so absence is visible, never imputed (R4).

func ReviewReportHead added in v0.3.0

func ReviewReportHead(raw string) string

ReviewReportHead extracts just the recorded Git HEAD from a review report, or "" if there is no resolved HEAD line. The overwrite guard (spec 09 R2) uses it to detect a report already scaffolded for the current commit — even before the auditor has filled the verdict, so a re-scaffold never clobbers in-progress notes.

func ReviewReportPath added in v0.3.0

func ReviewReportPath(root, slug string) string

ReviewReportPath is the per-spec review report the auditor role fills.

func RewriteTaskStatusLine added in v0.3.0

func RewriteTaskStatusLine(raw []byte, id string, marker string) ([]byte, error)

func RolePrompt added in v0.3.0

func RolePrompt(role string) string

RolePrompt returns the role constitution for role, read from the embedded role files — the single source of truth also written to .specd/roles/ by WriteScaffold. An unknown role falls back to craftsman.

func SaveProgram

func SaveProgram(path string, program Program) error

SaveProgram writes the program atomically at the current schema version.

func SaveState

func SaveState(path string, state State) error

func SaveStateCAS added in v0.3.0

func SaveStateCAS(path string, expectedRevision int64, state State) error

func SerializeTasksMd added in v0.3.0

func SerializeTasksMd(doc TasksMd) []byte

SerializeTasksMd preserves the source bytes exactly. The task DAG uses parsed rows for decisions, never rewritten markdown.

func SortHistory added in v0.3.0

func SortHistory(events []HistoryEvent)

SortHistory orders events by timestamp, then by the (SourceRank, Seq) tie-break, giving a total order that is stable across runs (spec 13 R3).

func SortedReportTaskIDs added in v0.3.0

func SortedReportTaskIDs(model ReportModel) []string

func SpecMemoryPath added in v0.3.0

func SpecMemoryPath(root, slug string) string

SpecMemoryPath is the per-spec steering-memory store (RM.1).

func SpecdDir

func SpecdDir(root string) string

func StatePath added in v0.3.0

func StatePath(root, slug string) string

func SteeringMemoryPath added in v0.3.0

func SteeringMemoryPath(root string) string

SteeringMemoryPath is the shared steering store promotions land in (RM.3).

func SubmissionsPath added in v0.3.0

func SubmissionsPath(root, slug string) string

SubmissionsPath is the per-spec append-only submission ledger. It is kept separate from evidence/criteria so the terminal-submit audit trail is physically distinct from execution evidence.

func SubmitBlockers added in v0.3.0

func SubmitBlockers(model ReportModel, gateFailures []string) []string

SubmitBlockers enumerates why a spec is not submittable: every gate failure (rendered upstream, since the gate registry lives in a subpackage) plus every task that is not complete. An empty result means all gates are green and every task is done — the R1 precondition. It is a pure function of the model and the pre-rendered gate failures so it is unit-testable without running gates.

func SummaryHash added in v0.3.0

func SummaryHash(summary string) string

SummaryHash is the content address of a submission summary: a hex sha256. The ledger stores it so `report --history` (spec 13) can show exactly what was submitted without re-deriving or storing the full summary text.

func Unifiedish added in v0.3.0

func Unifiedish(change AssetChange) string

Unifiedish renders a minimal line-oriented diff for a managed asset change, so `--dry-run` shows what would change before any write (spec 11 R5). It is not a full unified diff — it flags added/removed lines by prefix, which is enough for an operator to see the managed-region delta.

func ValidPhase added in v0.3.0

func ValidPhase(phase Phase) bool

func ValidSlug added in v0.3.0

func ValidSlug(slug string) bool

func ValidStatus added in v0.3.0

func ValidStatus(status Status) bool

func ValidateSlug

func ValidateSlug(slug string) error

func WithSpecLock

func WithSpecLock[T any](root string, fn func() (T, error)) (T, error)

WithSpecLock serializes harness work for one spec root and permits reentry from the same goroutine.

func WriteScaffold added in v0.3.0

func WriteScaffold(root string) error

Types

type AgentHost added in v0.3.0

type AgentHost struct {
	Name    string
	Detect  string
	Plan    string
	Install string
	Inspect string
	Verify  string
}

func AgentHosts added in v0.3.0

func AgentHosts() []AgentHost

type Annotations added in v0.3.0

type Annotations struct {
	Tokens     int    `json:"tokens,omitempty"`
	Cost       string `json:"cost,omitempty"`
	DurationMs int    `json:"duration_ms,omitempty"`
}

Annotations is worker-reported cost telemetry attached verbatim to a record. The doctrine is "stored, never computed" (spec 10 R1): specd accepts these values from the host worker and never estimates, derives, or counts tokens itself. Every field is optional (R5) — a worker that cannot report cost still produces valid records. Cost is a decimal string with currency-agnostic semantics; aggregation uses exact rational math, never float64 (R6).

func ParseAnnotations added in v0.3.0

func ParseAnnotations(tokens, cost, durationMs string, present func(string) bool) (*Annotations, error)

ParseAnnotations reads the optional --tokens/--cost/--duration-ms flags. It returns nil (no telemetry) when none are present, and a validation error when any is malformed — the caller maps that to a fail-closed exit 2 (R2). Values are stored verbatim; nothing here computes a cost.

type AssetChange added in v0.3.0

type AssetChange struct {
	Name    string
	RelPath string
	Before  string
	After   string
}

AssetChange is one file a repair/refresh/dry-run would touch.

func ApplyManagedRepair added in v0.3.0

func ApplyManagedRepair(root string) ([]AssetChange, error)

ApplyManagedRepair writes every planned change atomically and returns the list of files it touched. It is the mutating counterpart to PlanManagedRepair.

func PlanManagedRepair added in v0.3.0

func PlanManagedRepair(root string) ([]AssetChange, error)

PlanManagedRepair computes the changes needed to bring every managed asset's region back in sync with the current templates, without writing anything. A file whose managed region already matches is not listed. This is the pure core of `init --repair`/`--refresh`/`--dry-run` (spec 11 R3/R4/R5).

type Command added in v0.3.0

type Command struct {
	Name        string `json:"name"`
	Usage       string `json:"usage"`
	Description string `json:"description"`
	Flags       []Flag `json:"flags,omitempty"`
	// AllowedPhases is the set of lifecycle phases the verb may run in. A verb
	// valid everywhere declares []Phase{PhaseAny} explicitly — nothing defaults
	// silently to unrestricted (spec 03 R1, R6).
	AllowedPhases []Phase `json:"allowed_phases,omitempty"`
	// ExitCodes documents every status the verb can return (spec 03 R1/B.3).
	ExitCodes []ExitCode `json:"exit_codes,omitempty"`
	// Examples is at least one runnable invocation (spec 03 R1).
	Examples []string `json:"examples,omitempty"`
	// SpecSlugArg is the positional-argument index (0-based) that carries the
	// spec slug for phase enforcement, or nil when the verb resolves no spec by
	// a fixed position. Dispatch only phase-checks verbs with a non-nil index
	// (spec 03 R2: "verbs that take no spec slug skip the check"). Not exported
	// to JSON — it is an internal dispatch hint, not part of the help contract.
	SpecSlugArg *int `json:"-"`
	// Deferred marks a registered verb whose implementation is intentionally
	// not wired yet. The dispatcher reports the deferral and exits 0; the
	// handler-parity test treats a deferred verb as satisfied.
	Deferred bool `json:"deferred,omitempty"`
}

Command describes one supported top-level command. This metadata is the single source of truth for help, dispatch enforcement, MCP tool schemas, and role prompts — no surface hand-restates command semantics (spec 03 C.8).

func CommandByName added in v0.3.0

func CommandByName(name string) (Command, bool)

CommandByName returns the command metadata for name, and whether it exists.

func (Command) AllowsPhase added in v0.3.0

func (c Command) AllowsPhase(phase Phase) bool

AllowsPhase reports whether the command may run in phase. A command that declares PhaseAny is unrestricted.

func (Command) FlagByName added in v0.3.0

func (c Command) FlagByName(name string) *Flag

FlagByName returns the flag metadata for name, or nil if the command has no such flag.

type Config

type Config struct {
	Version            string
	Agent              string
	Gates              GatesConfig
	Context            ContextConfig
	Orchestration      OrchestrationConfig
	Criteria           CriteriaConfig
	Review             ReviewConfig
	Submit             SubmitConfig
	Security           SecurityConfig
	Escalation         EscalationConfig
	PromotionThreshold int
}

Config is the deterministic runtime configuration used by the harness.

type ConfigParseError added in v0.3.0

type ConfigParseError struct {
	Line    int
	Message string
}

func (ConfigParseError) Error added in v0.3.0

func (e ConfigParseError) Error() string

type ConfigPaths

type ConfigPaths struct {
	Global  string
	Project string
}

type ContextConfig added in v0.3.0

type ContextConfig struct {
	MaxTokens int
}

type CriteriaConfig added in v0.3.0

type CriteriaConfig struct {
	Required bool
}

CriteriaConfig is the opt-in per-acceptance-criterion evidence ratchet. When Required is true, the completion approval gate refuses while any acceptance criterion lacks a current passing record (spec 04 R6). Default off so existing flows are unbroken.

type CriterionRecord

type CriterionRecord struct {
	Type      string `json:"type"`      // always "criterion" — discriminates the store
	Criterion string `json:"criterion"` // "<req>.<sub>", e.g. "1.2"
	Status    string `json:"status"`    // "pass" | "fail"
	Evidence  string `json:"evidence"`  // operator-supplied text or path
	GitHead   string `json:"git_head"`  // pinned commit, same discipline as verify (R3)
	Timestamp string `json:"timestamp"` // RFC3339, from the injectable Clock
	Actor     string `json:"actor"`
}

CriterionRecord attests a single acceptance criterion of an approved requirement. It is a distinct evidence type from a task verify record (EvidenceRecord): a criterion record carries operator-supplied evidence and is never produced by running a command, so it can never substitute for a task's passing verify record (spec 04 R7). Records are append-only — a later pass never erases a prior fail (R4).

func LoadCriteria added in v0.3.0

func LoadCriteria(path string) ([]CriterionRecord, error)

LoadCriteria reads the criterion ledger in append order, preserving history (fails retained after later passes). A missing file is an empty ledger.

type Diagnostic added in v0.3.0

type Diagnostic struct {
	Severity string
	Path     string
	Message  string
}

type EscalationConfig added in v0.2.0

type EscalationConfig struct {
	MaxVerifyFails int
}

EscalationConfig is the opt-in verify-failure ratchet (spec 06 R5). MaxVerifyFails is the count of consecutive failing verify records (since the last pass or override) that escalates a task and blocks its completion until a human clears it with `task <id> --override --reason`. Default 3; 0 disables the ratchet.

type EvidenceRecord added in v0.3.0

type EvidenceRecord struct {
	TaskID      string `json:"task_id"`
	Command     string `json:"command"`
	ExitCode    int    `json:"exit_code"`
	GitHead     string `json:"git_head"`
	EvidenceRef string `json:"evidence_ref,omitempty"`
	// Timestamp and Actor stamp the attempt so `report --history` (spec 13) can
	// replay verify attempts in time order alongside approvals and submissions.
	// Both are omitempty: records written before spec 13 carry neither and still
	// decode as fully valid evidence — they simply sort by append order.
	Timestamp string `json:"timestamp,omitempty"`
	Actor     string `json:"actor,omitempty"`
	// Telemetry is optional worker-reported cost, stored verbatim (spec 10). A
	// nil pointer means the worker reported none — never imputed as zero. Old
	// records predating telemetry decode to nil, so they stay fully valid (R5).
	Telemetry *Annotations `json:"telemetry,omitempty"`
}

func LoadEvidenceRecords added in v0.3.0

func LoadEvidenceRecords(path string) ([]EvidenceRecord, error)

LoadEvidenceRecords reads the evidence log in append order, preserving every attempt (unlike LoadEvidence, which keeps only the latest record per task). Telemetry aggregation needs the full history for per-attempt breakdown.

type ExitCode added in v0.3.0

type ExitCode struct {
	Code    int    `json:"code"`
	Meaning string `json:"meaning"`
}

ExitCode documents one exit status a command can return. The convention is 0 success, 1 gate/verify failure, 2 usage / fail-closed rejection; per-verb deviations are declared explicitly (spec 03 design notes).

type Flag added in v0.3.0

type Flag struct {
	Name        string   `json:"name"`
	TakesValue  bool     `json:"takes_value,omitempty"`
	Description string   `json:"description,omitempty"`
	Type        string   `json:"type,omitempty"`    // "bool" | "string"; empty ⇒ bool
	Enum        []string `json:"enum,omitempty"`    // allowed values (value flags only)
	Default     string   `json:"default,omitempty"` // documented default
}

Flag describes one command-line flag surfaced by help metadata. Enum and Default make the flag a machine-readable contract: dispatch validates values against Enum (spec 03 R3) and MCP maps Enum/Default into JSON Schema.

type FrontierTask added in v0.3.0

type FrontierTask struct {
	ID       string `json:"id"`
	Role     string `json:"role,omitempty"`
	Verify   string `json:"verify,omitempty"`
	Terminal string `json:"terminal,omitempty"`
}

func Frontier added in v0.3.0

func Frontier(tasks []TaskRow, status map[string]TaskRunStatus) ([]FrontierTask, error)

func FrontierExcluding added in v0.3.0

func FrontierExcluding(tasks []TaskRow, status map[string]TaskRunStatus, escalated map[string]bool) ([]FrontierTask, error)

FrontierExcluding is Frontier with an escalation filter: any task id present in escalated is dropped from the runnable frontier so neither `status` nor the Brain will pick it up until a human clears it with an override (spec 06 R2). A nil escalated set is exactly Frontier.

type GatesConfig added in v0.3.0

type GatesConfig struct {
	Verify string
}

type Handshake added in v0.3.0

type Handshake struct {
	Version string   `json:"version"`
	Agent   string   `json:"agent,omitempty"`
	Tools   []string `json:"tools"`
	// PaletteDigest and ConfigDigest let an agent detect that its cached command
	// palette or effective config has drifted from this binary's (spec 11 R6).
	// Both are SHA-256 over the canonical (stable-key-order) JSON.
	PaletteDigest string `json:"palette_digest"`
	ConfigDigest  string `json:"config_digest"`
}

func BootstrapHandshake added in v0.3.0

func BootstrapHandshake(config Config) Handshake

type HelpPayload added in v0.3.0

type HelpPayload struct {
	SchemaVersion int       `json:"schema_version"`
	Commands      []Command `json:"commands"`
}

HelpPayload is the stable machine-readable help contract emitted by `help --json`. SchemaVersion lets consumers detect shape changes.

func BuildHelpPayload added in v0.3.0

func BuildHelpPayload() HelpPayload

BuildHelpPayload assembles the full palette for `help --json`.

type HistoryEvent added in v0.3.0

type HistoryEvent struct {
	Timestamp string `json:"timestamp,omitempty"`
	Actor     string `json:"actor,omitempty"`
	Event     string `json:"event"`
	Reference string `json:"reference,omitempty"`
	GitHead   string `json:"git_head,omitempty"`

	// SourceRank and Seq are the deterministic tie-break keys (R3), never
	// serialized. When two events share a timestamp (or both lack one), order is
	// resolved first by SourceRank (a fixed per-source-type ordering) then by Seq
	// (the record's position within its source). The pair is unique across all
	// events for a spec, so the total order — and therefore the byte output — is
	// identical on every run.
	SourceRank int `json:"-"`
	Seq        int `json:"-"`
}

HistoryEvent is one replayed line of a spec's audit trail (spec 13 R1). It is projected from records that already exist on disk — approvals/decisions in state.json, criterion and submission ledgers, verify evidence, the ACP ledger — never from a new event store, and `report --history` writes nothing (R2).

Timestamp/Actor are shown "where recorded": a source that does not stamp them leaves them empty rather than inventing a value.

type ManagedAsset added in v0.3.0

type ManagedAsset struct {
	Name     string // logical asset id, e.g. "roles/craftsman.md"
	RelPath  string // path under the project root, e.g. ".specd/roles/craftsman.md"
	Version  int
	Template string
}

ManagedAsset is one specd-managed scaffold file (a role or steering template). Its Template is wrapped in stable marker comments so `init --repair`/`--refresh` can regenerate the managed region while leaving any user content outside the markers byte-for-byte untouched (spec 11 R2/R3/R4).

func ManagedAssets added in v0.3.0

func ManagedAssets() ([]ManagedAsset, error)

ManagedAssets enumerates the role and steering templates baked into the binary.

func (ManagedAsset) Block added in v0.3.0

func (a ManagedAsset) Block() string

Block is the marker-wrapped managed region for the asset at its current version.

func (ManagedAsset) Merge added in v0.3.0

func (a ManagedAsset) Merge(existing string) string

Merge returns existing with this asset's managed region replaced by the current template block (matching a marker of *any* version, so a refresh restamps an older region). Content outside the markers is preserved exactly; when no region is present the block is appended. A brand-new file becomes just the block.

type MarkdownTable added in v0.3.0

type MarkdownTable struct {
	Header []string
	Rows   [][]string
}

MarkdownTable stores one parsed pipe table while retaining the original bytes.

type MemFields added in v0.3.0

type MemFields struct {
	Key         string
	Pattern     string
	Detail      string
	Source      string
	Criticality string
	Related     string
}

MemFields is one steering-memory entry. Related is the raw comma-separated value as supplied on the CLI; RenderMemBlock formats it into wikilinks.

type Mode added in v0.3.0

type Mode string
const (
	ModeDefault Mode = "default"
	ModeAgent   Mode = "agent"
)

type NotFoundError

type NotFoundError struct {
	Start string
}

func (NotFoundError) Error added in v0.3.0

func (e NotFoundError) Error() string

func (NotFoundError) ExitCode added in v0.3.0

func (e NotFoundError) ExitCode() int

type OrchestrationConfig added in v0.3.0

type OrchestrationConfig struct {
	Enabled bool
	Model   string
}

type OverrideRecord added in v0.3.0

type OverrideRecord struct {
	TaskID         string `json:"task_id"`
	Reason         string `json:"reason"`
	Actor          string `json:"actor"`
	Timestamp      string `json:"timestamp"`
	PriorFailCount int    `json:"prior_fail_count"`
}

OverrideRecord is an append-only human clearance of an escalated task (spec 06 R3). It resets the verify-failure counter but does NOT complete the task or stand in for evidence: after an override the task still needs a passing verify record to complete (the no-bypass invariant). PriorFailCount pins how many consecutive fails were cleared, for the audit trail.

func LoadOverrides added in v0.3.0

func LoadOverrides(path string) ([]OverrideRecord, error)

LoadOverrides reads the override ledger in append order. A missing file is not an error (no overrides yet).

type Phase

type Phase string
const (
	PhasePerceive Phase = "perceive"
	PhaseAnalyze  Phase = "analyze"
	PhasePlan     Phase = "plan"
	PhaseExecute  Phase = "execute"
	PhaseVerify   Phase = "verify"
	PhaseReflect  Phase = "reflect"

	// PhaseAny is the sentinel a command declares when it is valid in every
	// lifecycle phase. It is never a real state phase (ValidPhase rejects it);
	// it exists only so command metadata declares "unrestricted" explicitly
	// rather than defaulting silently to it (spec 03 R6).
	PhaseAny Phase = "any"
)

func AdvanceStatus added in v0.3.0

func AdvanceStatus(current, next Status) (Phase, error)

func PhaseForStatus

func PhaseForStatus(status Status) Phase

type Program added in v0.3.0

type Program struct {
	SchemaVersion int           `json:"schema_version"`
	Links         []ProgramLink `json:"links"`
}

Program is the cross-spec dependency graph. It is stored at `.specd/program.json`, written atomically under the root lock (which already serializes all harness work for the root, so program state needs no second lock and cannot deadlock against a spec lock).

func LoadProgram

func LoadProgram(path string) (Program, error)

LoadProgram reads program.json. A missing file is an empty program at the current schema version. An unknown (future) schema is an error — fail closed rather than silently misread newer state.

func (p *Program) AddLink(from, to string)

AddLink records from→to. It is idempotent: a duplicate link is a no-op.

func (Program) Deps added in v0.3.0

func (p Program) Deps(slug string) []string

Deps returns the slugs that slug directly depends on (its To edges), sorted.

func (Program) Frontier added in v0.3.0

func (p Program) Frontier(specs []string, complete func(string) bool) []string

Frontier returns the specs that are actionable now: not yet complete and with every dependency complete. complete is injected by the caller (the same all-gates-green + all-tasks-complete predicate `submit` uses), keeping this pure over the graph with no gate logic in core (spec 12 R4).

func (p Program) HasLink(from, to string) bool

HasLink reports whether from→to is already recorded.

func (Program) IncompleteDeps added in v0.3.0

func (p Program) IncompleteDeps(slug string, complete func(string) bool) []string

IncompleteDeps returns slug's direct dependencies that are not yet complete — the specs blocking it from executing (spec 12 R5).

func (p *Program) RemoveLink(from, to string) bool

RemoveLink deletes from→to and reports whether it existed.

func (Program) WouldCycle added in v0.3.0

func (p Program) WouldCycle(from, to string) []string

WouldCycle reports the cycle path that adding from→to would create, or nil if the link is safe. A cycle exists when To already depends (transitively) on From: following dependency edges from To reaches From. The returned path reads from→to→…→from for printing (spec 12 R2).

type ProgramLink struct {
	From string `json:"from"`
	To   string `json:"to"`
}

ProgramLink records that From depends on To — To must reach completion before From may execute. Links live at the program level, never inside a spec's state.json, so each file keeps a single writer (spec 12 R6).

type PrometheusMetrics added in v0.3.0

type PrometheusMetrics struct {
	Slug            string
	TasksByStatus   map[string]int
	VerifyAttempts  int
	VerifyFailures  int
	CriteriaPassing int
	CriteriaTotal   int
	EscalatedTasks  int
	Tokens          int
	Cost            string // exact decimal string; "" renders as 0
	DurationMs      int
}

PrometheusMetrics is the pure, on-disk-derived snapshot rendered as a Prometheus textfile exposition (spec 13 R4). It is assembled by the caller from the same state.json + ledgers the rest of `report` reads; RenderMetrics here adds no I/O and no gate logic.

Metric names are an API: renaming one breaks every dashboard built against it, so the contract is written down in docs/command-reference.md and must not churn. All names carry the `specd_` prefix, snake_case, with `_total` on monotonic counters and `_seconds` on durations, per Prometheus conventions.

type Record added in v0.3.0

type Record struct {
	Kind             string `json:"kind"`
	Text             string `json:"text,omitempty"`
	Scope            string `json:"scope,omitempty"`
	Gate             string `json:"gate,omitempty"`
	ApprovedRevision int64  `json:"approved_revision,omitempty"`
	Timestamp        string `json:"timestamp"`
	GitHead          string `json:"git_head"`
	Actor            string `json:"actor"`
}

Record is a stamped ledger entry stored in State.Records. Content fields (Text/Scope/Gate/ApprovedRevision) are per-kind; StampRecord fills the provenance triple (Timestamp/GitHead/Actor) that ADR-6 observability and PROJECT.md §3 evidence integrity require on every record.

func StampRecord added in v0.3.0

func StampRecord(rec Record, gitHead string) Record

StampRecord fills the provenance triple on rec: an RFC 3339 timestamp from the injectable Clock, the caller-resolved git HEAD, and the host actor. The actor is host-reported and stored verbatim — never trusted as proof.

type ReportModel added in v0.3.0

type ReportModel struct {
	Slug     string       `json:"slug"`
	Total    int          `json:"total"`
	Complete int          `json:"complete"`
	Pending  int          `json:"pending"`
	Blocked  int          `json:"blocked"`
	Running  int          `json:"running"`
	Tasks    []ReportTask `json:"tasks"`
}

func BuildReportModel added in v0.3.0

func BuildReportModel(slug string, tasks []TaskRow, status map[string]TaskRunStatus, evidence map[string]EvidenceRecord) ReportModel

type ReportTask added in v0.3.0

type ReportTask struct {
	ID          string        `json:"id"`
	Status      TaskRunStatus `json:"status"`
	Role        string        `json:"role,omitempty"`
	Files       string        `json:"files,omitempty"`
	Verify      string        `json:"verify,omitempty"`
	Acceptance  string        `json:"acceptance,omitempty"`
	EvidenceRef string        `json:"evidence_ref,omitempty"`
}

type ReviewConfig added in v0.3.0

type ReviewConfig struct {
	Required bool
}

ReviewConfig is the opt-in review gate (spec 09). When Required is true, the completion approval gate refuses unless review_report.md carries an approve verdict recorded at the current git HEAD. Default off so existing flows are unbroken.

type ReviewReport added in v0.2.0

type ReviewReport struct {
	Verdict  string
	Head     string
	Findings string
}

ReviewReport is the field-extraction result of a review_report.md. The report is human-edited, so — unlike the tasks parser — the parser does not require byte-stability or round-tripping; it extracts three load-bearing fields and the findings prose. The Head field is what makes an approval a *fact about this code* (spec 09 R3), mirroring evidence pinning.

func ParseReviewReport added in v0.2.0

func ParseReviewReport(raw string) (ReviewReport, error)

ParseReviewReport extracts the verdict, HEAD, and findings from a review report. It is strict (R5): a missing/unknown verdict or a missing HEAD line is an error, never a silent approve. It is tolerant of surrounding human edits — it scans for the labelled fields rather than requiring a fixed byte layout.

type SecurityConfig added in v0.3.0

type SecurityConfig struct {
	Secrets   string
	Injection string
	Slopsquat string
}

SecurityConfig sets per-scanner severity for the opt-in security gate (spec 05 R5). Each field is off|warn|error: error findings fail the gate (exit 1), warn findings print but pass, off skips the scanner. Defaults tuned so a real secret blocks while noisier heuristics only warn.

type State

type State struct {
	SchemaVersion int                        `json:"schema_version"`
	Slug          string                     `json:"slug"`
	Mode          Mode                       `json:"mode"`
	Status        Status                     `json:"status"`
	Phase         Phase                      `json:"phase"`
	Revision      int64                      `json:"revision"`
	Records       map[string]json.RawMessage `json:"records,omitempty"`
	// TaskStatus is the machine truth for per-task run status (ADR-1: status
	// lives in state.json, tasks.md stays clean Markdown). The Sync gate
	// enforces that tasks.md markers agree with this map.
	TaskStatus map[string]TaskRunStatus   `json:"task_status,omitempty"`
	Extra      map[string]json.RawMessage `json:"extra,omitempty"`
}

func InitialState

func InitialState(slug string) State

func LoadState

func LoadState(path string) (State, error)

func MigrateState added in v0.3.0

func MigrateState(state State) (State, error)

func (State) Validate added in v0.3.0

func (s State) Validate() error

type Status added in v0.3.0

type Status string
const (
	StatusRequirements Status = "requirements"
	StatusDesign       Status = "design"
	StatusTasks        Status = "tasks"
	StatusExecuting    Status = "executing"
	StatusVerifying    Status = "verifying"
	StatusComplete     Status = "complete"
	StatusBlocked      Status = "blocked"
)

type SubmissionRecord added in v0.3.0

type SubmissionRecord struct {
	Type        string `json:"type"`         // always "submission" — discriminates the store
	GitHead     string `json:"git_head"`     // commit the summary described
	SummaryHash string `json:"summary_hash"` // sha256 of the streamed summary
	Command     string `json:"command"`      // configured submit.command ("" = dry-run)
	Exit        int    `json:"exit"`         // command exit code (0 for dry-run)
	Timestamp   string `json:"timestamp"`    // RFC3339, from the injectable Clock
	Actor       string `json:"actor"`
}

SubmissionRecord is one terminal submission of a spec: the deterministic PR summary (identified by its content hash) that was streamed to the operator-configured command, pinned to the git HEAD it described and the command's exit code. Records are append-only — a resubmission never erases a prior one, so the ledger is a full audit trail (spec 08 R4, feeds spec 13).

func LoadSubmissions added in v0.3.0

func LoadSubmissions(path string) ([]SubmissionRecord, error)

LoadSubmissions reads the submission ledger in append order. A missing file is an empty ledger.

type SubmitConfig added in v0.3.0

type SubmitConfig struct {
	Command     string
	TimeoutSecs int
}

SubmitConfig configures the terminal `submit` verb (spec 08). Command is an operator-supplied shell line run through the shared exec path with the PR summary streamed on stdin; empty means dry-run (print summary, exit 0). The binary embeds no git/GitHub logic — the operator owns transport. TimeoutSecs bounds the command; zero applies SubmitDefaultTimeoutSecs.

type TaskDAG added in v0.3.0

type TaskDAG struct {
	Tasks []TaskRow
	ByID  map[string]TaskRow
}

func NewTaskDAG added in v0.3.0

func NewTaskDAG(tasks []TaskRow) (TaskDAG, error)

func (TaskDAG) AllBlocked added in v0.3.0

func (dag TaskDAG) AllBlocked(status map[string]TaskRunStatus) bool

func (TaskDAG) RunnableFrontier added in v0.3.0

func (dag TaskDAG) RunnableFrontier(status map[string]TaskRunStatus) ([]string, error)

func (TaskDAG) TopologicalWaves added in v0.3.0

func (dag TaskDAG) TopologicalWaves() ([][]string, error)

type TaskRow added in v0.3.0

type TaskRow struct {
	ID         string
	Marker     string
	Role       string
	Files      string
	DependsOn  []string
	Verify     string
	Acceptance string
}

type TaskRunStatus added in v0.3.0

type TaskRunStatus string
const (
	TaskPending  TaskRunStatus = "pending"
	TaskRunning  TaskRunStatus = "running"
	TaskComplete TaskRunStatus = "complete"
	TaskBlocked  TaskRunStatus = "blocked"
)

type TaskTelemetry added in v0.3.0

type TaskTelemetry struct {
	TaskID       string        `json:"task_id"`
	Attempts     []Annotations `json:"attempts,omitempty"`
	Tokens       int           `json:"tokens"`
	Cost         string        `json:"cost"`
	DurationMs   int           `json:"duration_ms"`
	HasTelemetry bool          `json:"has_telemetry"`
}

TaskTelemetry is one task's telemetry across every recorded attempt. Absence is explicit: HasTelemetry is false when no attempt carried annotations, so the report shows missing data rather than imputing zero (R4).

type TasksMd added in v0.3.0

type TasksMd struct {
	Raw    []byte
	Tasks  []TaskRow
	Tables []MarkdownTable
}

TasksMd is the parsed representation of a tasks.md file. SerializeTasksMd returns Raw unchanged unless a caller deliberately builds a new value.

func ParseTasksMd

func ParseTasksMd(raw []byte) (TasksMd, error)

type TelemetryReport added in v0.3.0

type TelemetryReport struct {
	Tokens     int             `json:"tokens"`
	Cost       string          `json:"cost"`
	DurationMs int             `json:"duration_ms"`
	Tasks      []TaskTelemetry `json:"tasks,omitempty"`
	Missing    []string        `json:"missing,omitempty"`
}

TelemetryReport aggregates annotations per spec and per task. Totals are exact (integer sums for tokens/duration, rational sum for cost). Missing lists the tasks with no telemetry at all.

func AggregateTelemetry added in v0.3.0

func AggregateTelemetry(records []EvidenceRecord, taskOrder []string) TelemetryReport

AggregateTelemetry folds every evidence record's annotations into per-task and per-spec totals. records is the full append-order evidence log (one entry per attempt), so per-attempt breakdown is preserved. taskOrder fixes the task ordering for deterministic output; a task with no annotated attempt is listed in Missing and never imputed a zero cost.

type ToolPolicy added in v0.3.0

type ToolPolicy struct {
	Optional map[string]bool
}

func LoadToolPolicy added in v0.3.0

func LoadToolPolicy(path string) ToolPolicy

type Wave added in v0.3.0

type Wave struct {
	Index int      `json:"index"`
	Tasks []string `json:"tasks"`
}

func ProjectWaves added in v0.3.0

func ProjectWaves(tasks []TaskRow) ([]Wave, error)

Directories

Path Synopsis
security
Package security implements the opt-in deterministic security gate (spec 05).
Package security implements the opt-in deterministic security gate (spec 05).

Jump to

Keyboard shortcuts

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