--- 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]()