---
title: internal/fixit
description: Go API reference for the fixit package.
---
# fixit
```go
import "github.com/jbcom/radioactive-ralph/internal/fixit"
```
Package fixit implements the deliberate plan\-creation pipeline for fixit\-ralph's advisor mode.
See docs/design/fixit\-plan\-pipeline.md for the architectural rationale. Each stage in this package corresponds to a stage in the design doc.
## Index
- [Constants](<#constants>)
- [type AnalyzeOptions](<#AnalyzeOptions>)
- [type DocFile](<#DocFile>)
- [type EmitResult](<#EmitResult>)
- [func EmitToDAG\(ctx context.Context, o EmitToDAGOpts\) \(EmitResult, error\)](<#EmitToDAG>)
- [type EmitToDAGOpts](<#EmitToDAGOpts>)
- [type EmittedPlan](<#EmittedPlan>)
- [func Emit\(plansDir, topic string, proposal PlanProposal, validation ValidationResult, status PlanStatus, intent IntentSpec, rc RepoContext\) \(EmittedPlan, error\)](<#Emit>)
- [func EmitFallback\(plansDir, topic string, reason string, rawOutput string, intent IntentSpec, rc RepoContext\) \(EmittedPlan, error\)](<#EmitFallback>)
- [func RunPipeline\(ctx context.Context, opts RunOptions\) \(EmittedPlan, error\)](<#RunPipeline>)
- [type GHIssue](<#GHIssue>)
- [type GitCommit](<#GitCommit>)
- [type IntentOptions](<#IntentOptions>)
- [type IntentSpec](<#IntentSpec>)
- [func CaptureIntent\(opts IntentOptions\) \(IntentSpec, error\)](<#CaptureIntent>)
- [type InventorySnapshot](<#InventorySnapshot>)
- [type PlanProposal](<#PlanProposal>)
- [func Analyze\(ctx context.Context, opts AnalyzeOptions\) \(PlanProposal, error\)](<#Analyze>)
- [type PlanStatus](<#PlanStatus>)
- [type RefineIteration](<#RefineIteration>)
- [type RefineOptions](<#RefineOptions>)
- [type RefineResult](<#RefineResult>)
- [func Refine\(ctx context.Context, rc RepoContext, intent IntentSpec, opts RefineOptions\) \(RefineResult, error\)](<#Refine>)
- [type RepoContext](<#RepoContext>)
- [func Explore\(ctx context.Context, repoRoot string\) \(RepoContext, error\)](<#Explore>)
- [type RunOptions](<#RunOptions>)
- [type Task](<#Task>)
- [type ValidationResult](<#ValidationResult>)
- [func Validate\(p PlanProposal, rc RepoContext, intent IntentSpec\) ValidationResult](<#Validate>)
- [type VariantScore](<#VariantScore>)
- [func Score\(rc RepoContext, intent IntentSpec\) \[\]VariantScore](<#Score>)
## Constants
MinConfidence is the floor below which a plan becomes provisional regardless of other rules passing.
```go
const MinConfidence = 50
```
## type [AnalyzeOptions]()
AnalyzeOptions feeds Analyze.
```go
type AnalyzeOptions struct {
Intent IntentSpec
RC RepoContext
Scores []VariantScore
// ClaudeBin overrides the default `claude` binary path. Tests use
// the cassette replayer or the fake-claude double here.
ClaudeBin string
// WorkingDir is the cwd for the spawned subprocess. Defaults to the
// repo root from RC.GitRoot.
WorkingDir string
// Model pins the tier for the planning subprocess. Empty defaults
// to "opus" — the advisor runs infrequently (once per plan), so
// the cost is bounded and the quality delta is meaningful.
Model string
// Effort pins the reasoning-effort level. Empty defaults to "high"
// so opus scales up on genuinely hard repos without burning tokens
// on simple ones.
Effort string
// Timeout caps the total Claude analysis time. Default 180s —
// opus with auto-effort can take longer than the old sonnet/medium
// defaults, so the cap is lifted from 90s accordingly.
Timeout time.Duration
}
```
## type [DocFile]()
DocFile records a markdown file's frontmatter and basic metadata so Stage 4 can reason about doc freshness without re\-reading every file.
```go
type DocFile struct {
Path string
Frontmatter map[string]string
UpdatedISO string // from frontmatter or file mtime fallback
}
```
## type [EmitResult]()
EmitResult is what EmitToDAG returns.
```go
type EmitResult struct {
PlanID string
Status PlanStatus
TaskIDs []string
Proposal PlanProposal
Validation ValidationResult
}
```
### func [EmitToDAG]()
```go
func EmitToDAG(ctx context.Context, o EmitToDAGOpts) (EmitResult, error)
```
EmitToDAG writes the fixit output into plandag. Creates:
- one plans row \(status mapped from fixit PlanStatus\)
- one intents row capturing raw operator input
- one analyses row capturing Claude's Phase 2 output
- N tasks rows \(one per proposal.Tasks entry\)
- topologically\-sorted dependency edges when Task.DependsOn is set
Returns the newly\-generated plan UUID plus the slugified task IDs.
## type [EmitToDAGOpts]()
EmitToDAGOpts configures EmitToDAG.
```go
type EmitToDAGOpts struct {
Store *plandag.Store
Topic string
Proposal PlanProposal
Validation ValidationResult
Status PlanStatus
Intent IntentSpec
RC RepoContext
RawOutput string // Phase 2 Claude output; stored in analyses.raw_json
}
```
## type [EmittedPlan]()
EmittedPlan describes what Stage 6 wrote.
```go
type EmittedPlan struct {
Path string
Status PlanStatus
Proposal PlanProposal // zero-valued when Status=fallback
Validation ValidationResult
Intent IntentSpec
RepoContext RepoContext
}
```
### func [Emit]()
```go
func Emit(plansDir, topic string, proposal PlanProposal, validation ValidationResult, status PlanStatus, intent IntentSpec, rc RepoContext) (EmittedPlan, error)
```
Emit runs Stage 6 — write the plan to disk and return what happened. The status comes from the caller \(Current / Provisional / Fallback\) based on Stage 4 \+ Stage 5 outcomes.
The emitted file ALWAYS has valid plan\-format frontmatter so it satisfies the shape the plans\-first discipline expects, even when status is fallback or provisional — other variants gate on the status field, not on presence of frontmatter.
### func [EmitFallback]()
```go
func EmitFallback(plansDir, topic string, reason string, rawOutput string, intent IntentSpec, rc RepoContext) (EmittedPlan, error)
```
EmitFallback writes a diagnostic\-only plan whose status is \`fallback\`. Used when Stage 4 fails twice or Stage 5 produces a failure the operator should see directly.
### func [RunPipeline]()
```go
func RunPipeline(ctx context.Context, opts RunOptions) (EmittedPlan, error)
```
RunPipeline orchestrates Stages 1\-6 and returns what was emitted. Errors here indicate an unrecoverable pipeline failure \(e.g. can't explore the repo\); Stage 4/5 failures become fallback or provisional plans, not errors.
## type [GHIssue]()
GHIssue is the subset of \`gh pr/issue list \-\-json\` fields fixit uses.
```go
type GHIssue struct {
Number int
Title string
Draft bool
State string // OPEN, CLOSED, MERGED
MergeStatus string // CLEAN, DIRTY, BLOCKED, UNKNOWN
Author string
Labels []string
}
```
## type [GitCommit]()
GitCommit is a single entry from \`git log \-\-oneline\` enriched with author \+ date.
```go
type GitCommit struct {
SHA string
Subject string
Author string
DateISO string
}
```
## type [IntentOptions]()
IntentOptions feeds CaptureIntent.
```go
type IntentOptions struct {
Topic string
Description string
Constraints []string
NonInteractive bool
RepoRoot string
Stdin io.Reader // defaults to os.Stdin
Stdout io.Writer // defaults to os.Stdout
}
```
## type [IntentSpec]()
IntentSpec captures Stage 1 output — what the operator is trying to accomplish and what's off\-limits.
```go
type IntentSpec struct {
// Topic is the sanitized slug used in the output filename.
Topic string
// Description is free-form operator text describing the goal.
// Either passed via --description, scraped from a TOPIC.md at the
// repo root, or empty.
Description string
// Constraints are operator-declared hard limits. Examples: "no
// opus", "stay under $5", "main branch only", "weekend only".
// Stage 4 renders these as inviolable rules in the prompt; Stage
// 5 rejects proposals that violate them.
Constraints []string
// AnswersToQs is the populated record of any interactive
// questions the operator answered. Empty in non-interactive mode.
AnswersToQs map[string]string
}
```
### func [CaptureIntent]()
```go
func CaptureIntent(opts IntentOptions) (IntentSpec, error)
```
CaptureIntent runs Stage 1. In non\-interactive mode it returns immediately with whatever the caller supplied. In interactive mode it asks three short questions on stdout and reads answers from stdin. Either way it consults a TOPIC.md at the repo root for an operator\-prepared description if \-\-description wasn't passed.
## type [InventorySnapshot]()
InventorySnapshot is a flattened view of the operator capability inventory \(helper integrations, MCPs, agents\). The full inventory.Inventory has more detail but the advisor only needs names and high\-level availability.
```go
type InventorySnapshot struct {
Skills []string // FullName form, e.g. "coderabbit:review"
MCPs []string
Agents []string
}
```
## type [PlanProposal]()
PlanProposal is Stage 4 output — the structured JSON the constrained Claude subprocess returns.
```go
type PlanProposal struct {
Primary string `json:"primary"`
PrimaryRationale string `json:"primary_rationale"`
Alternate string `json:"alternate,omitempty"`
AlternateWhen string `json:"alternate_when,omitempty"`
Tasks []Task `json:"tasks"`
AcceptanceCriteria []string `json:"acceptance_criteria"`
Confidence int `json:"confidence"` // 0..100
}
```
### func [Analyze]()
```go
func Analyze(ctx context.Context, opts AnalyzeOptions) (PlanProposal, error)
```
Analyze runs Stage 4. Returns a parsed PlanProposal on success, or \(zero, error\) on hard failure. Callers handle fallback emission; this function never returns a half\-filled proposal.
One retry on JSON\-parse failure: when Claude returns text that doesn't unmarshal cleanly, we re\-spawn with the parse error appended so the model can self\-correct. Second failure bubbles up.
## type [PlanStatus]()
PlanStatus captures whether the emitted plan satisfies the plans\- first discipline gate.
```go
type PlanStatus string
```
```go
const (
// StatusCurrent means the plan passed every validation rule and
// other variants will accept it as a valid durable plan.
StatusCurrent PlanStatus = "current"
// StatusProvisional means at least one validation rule failed but
// the proposal had enough merit to write. Other variants refuse to
// run on a provisional plan until the operator promotes it.
StatusProvisional PlanStatus = "provisional"
// StatusFallback means Stage 4 (Claude analysis) failed twice. The
// emitted file is a diagnostic, not a plan.
StatusFallback PlanStatus = "fallback"
)
```
## type [RefineIteration]()
RefineIteration records what happened in one pass.
```go
type RefineIteration struct {
Iteration int
Proposal PlanProposal
Validation ValidationResult
Confidence int
Accepted bool
}
```
## type [RefineOptions]()
RefineOptions drives the Stage 4 \+ Stage 5 refinement loop.
```go
type RefineOptions struct {
AnalyzeOpts AnalyzeOptions
MaxIterations int // default 3
MinConfidenceThreshold int // default 70
StopOnValidationPass bool // default true — stop as soon as validation passes
}
```
## type [RefineResult]()
RefineResult captures each iteration the loop produced, plus the final accepted proposal \(or zero value if all iterations failed\).
```go
type RefineResult struct {
Iterations int // how many passes we actually ran
FinalProposal PlanProposal // the accepted or last-attempt proposal
FinalValidation ValidationResult
History []RefineIteration
AcceptedAt int // iteration number of the accepted proposal (1-indexed), 0 if never
}
```
### func [Refine]()
```go
func Refine(ctx context.Context, rc RepoContext, intent IntentSpec, opts RefineOptions) (RefineResult, error)
```
Refine runs Stage 4 repeatedly, feeding validation failures back into each subsequent call as corrective context. Stops when any of:
- Proposal validates AND confidence \>= MinConfidenceThreshold
- MaxIterations reached
- Hard error \(context cancel, claude spawn fail\)
The LLM sees each prior attempt's failures appended to the system prompt, so it can deliberately address them rather than randomly redrafting.
## type [RepoContext]()
RepoContext is Stage 2 output — everything the deterministic exploration discovered about the repo.
```go
type RepoContext struct {
GitRoot string
CurrentBranch string
DefaultBranch string
OnDefaultBranch bool
Commits []GitCommit
DocsPresent []DocFile
DocsStale []string
DocsMissing []string
PlansDir string
PlansIndexExists bool
PlansIndexFM map[string]string
PlansFiles []string
GHAuthenticated bool
OpenPRs []GHIssue
OpenIssues []GHIssue
AIWelcomeIssues []GHIssue
Inventory InventorySnapshot
LangCounts map[string]int
GovernanceMissing []string
}
```
### func [Explore]()
```go
func Explore(ctx context.Context, repoRoot string) (RepoContext, error)
```
Explore runs Stage 2 — deterministic repo exploration. Every field in the returned RepoContext comes from a shell\-out \(git, gh, file walk\) — zero LLM calls.
## type [RunOptions]()
RunOptions drives the full six\-stage pipeline.
```go
type RunOptions struct {
RepoRoot string
Topic string
Description string
Constraints []string
NonInteractive bool
// ClaudeBin overrides the default `claude` binary for Stage 4.
// Tests pass the cassette replayer or fake-claude here.
ClaudeBin string
// SkipAnalysis bypasses Stage 4 — useful only for tests that
// exercise the deterministic stages without spawning a
// subprocess. When true, the pipeline returns after Stage 3 with
// a zero PlanProposal.
SkipAnalysis bool
// MaxRefinementIterations caps how many rounds of Claude
// refinement Stage 4 will do before giving up. Default 3.
// Configurable via CLI flag or config.toml [variants.fixit]
// max_refinement_iterations.
MaxRefinementIterations int
// MinConfidenceThreshold is the confidence floor a proposal must
// meet before we stop refining. Validate.MinConfidence is the
// lower absolute floor below which validation fails; this
// threshold is the refinement-loop bar. Default 70.
// Configurable via CLI flag or config.toml [variants.fixit]
// min_confidence_threshold.
MinConfidenceThreshold int
// PlanModel pins the Claude model tier for Stage 4 planning.
// Empty defaults to "opus" — planning benefits from the most
// capable tier and the advisor runs infrequently. Configurable
// via CLI (--plan-model) or config.toml [variants.fixit]
// plan_model.
PlanModel string
// PlanEffort pins the reasoning-effort level for Stage 4.
// Empty defaults to "high". Configurable via CLI (--plan-effort)
// or config.toml [variants.fixit] plan_effort.
PlanEffort string
}
```
## type [Task]()
Task is one item in a PlanProposal's task list. The DAG\-oriented fields \(VariantHint, ContextBoundary, AcceptanceCriteria, DependsOn\) are populated when Stage 4 emits enough structure to build a real DAG. When omitted, EmitToDAG falls back to an implicit linear chain.
```go
type Task struct {
Title string `json:"title"`
Effort string `json:"effort"` // S | M | L
Impact string `json:"impact"` // S | M | L
VariantHint string `json:"variant_hint,omitempty"`
ContextBoundary bool `json:"context_boundary,omitempty"`
AcceptanceCriteria []string `json:"acceptance_criteria,omitempty"`
DependsOn []string `json:"depends_on,omitempty"`
}
```
## type [ValidationResult]()
ValidationResult is Stage 5 output — whether the proposal passed every rule and, if not, what failed.
```go
type ValidationResult struct {
Passed bool
Failures []string
}
```
### func [Validate]()
```go
func Validate(p PlanProposal, rc RepoContext, intent IntentSpec) ValidationResult
```
Validate runs Stage 5. Returns \(passed, failures\). A passing validation means the plan gets \`status: current\`; a failing validation still emits the plan but downgrades status to \`provisional\` so other variants' plans\-first gate refuses it.
## type [VariantScore]()
VariantScore is one entry in Stage 3's deterministic ranking.
```go
type VariantScore struct {
Variant string
Score int // 0..100
Reasons []string // human-readable bullets, fed into prompt
Disqualifying []string // hard exclusions; non-empty means score=0
}
```
### func [Score]()
```go
func Score(rc RepoContext, intent IntentSpec) []VariantScore
```
Score runs Stage 3 — deterministic variant ranking. Every variant gets a 0..100 score with bullet\-justified Reasons. Disqualifying notes set the score to 0 \(e.g. world\-breaker without \-\-confirm\-burn\-everything from the CLI, since gated variants can't be auto\-handed\-off\).
Same input always produces same output. Rules live here so they can be unit\-tested independently of Claude.
Generated by [gomarkdoc]()