onboarding

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package onboarding implements the shared logic for the krit init onboarding flow. It is consumed by both the bash/gum prototype (scripts/krit-init.sh, via the krit binary) and by the bubbletea TUI in cmd/krit/init.go.

The package has three concerns:

  • Loading and validating config/onboarding/controversial-rules.json (the Registry type below).
  • Scanning a target directory with each shipped profile and parsing the JSON summary (profiles.go).
  • Generating a merged krit.yml from a chosen profile plus the user's answers to the questionnaire (writer.go).

The gum script predates this package and still uses yq/jq directly. When the two diverge in behavior, this package is the source of truth — the TUI is the production implementation.

Index

Constants

This section is empty.

Variables

View Source
var ProfileNames = []string{"strict", "balanced", "relaxed", "detekt-compat"}

ProfileNames lists the onboarding profiles in presentation order (tightest → loosest). This order also determines the order of rows in the comparison table.

View Source
var StrictStages = []struct {
	Prefix string
	Label  string
}{
	{"verbose: Found", "discovered files"},
	{"verbose: Type resolver", "type resolver ready"},
	{"verbose: Running", "rules loaded"},
	{"verbose: Cache:", "cache loaded"},
	{"verbose: Parsed", "parsed sources"},
	{"verbose: Type-indexed", "type indexed"},
	{"verbose: Cross-file", "cross-file analyzed"},
	{"verbose: Analyzed", "rules executed"},
	{"verbose: Android project", "android analyzed"},
}

StrictStages lists the ordered sub-stages emitted by `krit -v` to stderr during a scan. The TUI uses this to render a live progress bar for the strict profile scan (the slowest, cold-cache one). Each entry pairs a stderr-line prefix with a short human label. Stages that don't fire in smaller projects (e.g. Android-only) are still listed — the progress bar simply advances when it sees them.

Functions

func ProfilePath

func ProfilePath(repoRoot, name string) string

ProfilePath returns the path to a profile YAML file for the given profile name, rooted at repoRoot/config/profiles/.

func ScanAllProfiles

func ScanAllProfiles(ctx context.Context, opts ScanOptions) (map[string]*ScanResult, error)

ScanAllProfiles scans the target with every profile in ProfileNames sequentially. Returns a map keyed by profile name. The sequential execution lets krit's incremental cache warm up — the first scan parses every file, the rest only re-evaluate rules.

func WriteConfig

func WriteConfig(opts WriteConfigOptions) ([]byte, error)

WriteConfig returns the merged krit.yml bytes. It deep-merges the profile template with the override tree, then prepends a header comment documenting the profile and override count.

This is the Go equivalent of the gum script's `yq eval-all '. as $item ireduce ({}; . * $item)'` invocation.

func WriteConfigFile

func WriteConfigFile(targetDir string, opts WriteConfigOptions) (string, error)

WriteConfigFile is a thin wrapper around WriteConfig that also handles backup of an existing krit.yml and 0644 file creation.

func YAMLUnmarshalMap

func YAMLUnmarshalMap(data []byte, out *map[string]interface{}) error

YAMLUnmarshalMap is a thin wrapper so callers outside this package don't need to import gopkg.in/yaml.v3 directly. Used by the TUI to read threshold values out of profile templates.

Types

type Answer

type Answer struct {
	QuestionID string
	Value      bool // true = yes, false = no
	Cascaded   bool // true if derived from a parent answer
	Parent     string
}

Answer represents a resolved yes/no answer to one question, along with whether the answer was user-chosen (interactive) or cascaded from a parent question.

func ResolveAnswers

func ResolveAnswers(reg *Registry, profile string, ask func(q *Question, defaultYes bool) bool) ([]Answer, error)

ResolveAnswers walks the registry in declaration order and fills in answers for every question. Interactive callers pass an `ask` function that returns the user's yes/no choice for a question; non-interactive callers (CI, tests) can pass a closure that returns the per-profile default.

Cascade semantics: when a parent question is answered, every child whose cascade_from == parent is given a derived answer using the per-profile default of the parent's answer-bucket. Children are added to the cascaded set so `ask` is never called for them.

parent answered "yes" → each child uses its strict default
parent answered "no"  → each child uses its relaxed default

This matches the gum script's apply_cascades behavior.

type FindingSample

type FindingSample struct {
	File    string
	Line    int
	Message string
}

FindingSample is a trimmed finding stored for TUI preview. The explorer right pane shows at most 3 per rule — only file, line, and message are kept.

type Override

type Override struct {
	Ruleset string
	Rule    string
	Active  bool
}

Override is one ruleset+rule active state to merge into a profile.

func BuildOverrides

func BuildOverrides(reg *Registry, answers []Answer) []Override

BuildOverrides translates resolved answers into override tuples. Questions with an empty rules list (parent-only metas) produce no overrides on their own — their children do the work.

type ProgressEvent

type ProgressEvent struct {
	// StageIndex is the 1-based index of the stage just completed
	// (1..len(StrictStages)).
	StageIndex int
	// StageLabel is the human-readable label from StrictStages.
	StageLabel string
	// TotalStages is len(StrictStages), duplicated here so callers
	// don't need to import the slice.
	TotalStages int
}

ProgressEvent is emitted by ScanProfileWithProgress as each verbose-stderr stage marker is seen.

type Question

type Question struct {
	ID              string          `json:"id"`
	Question        string          `json:"question"`
	Rationale       string          `json:"rationale"`
	Rules           []string        `json:"rules"`
	CascadeFrom     *string         `json:"cascade_from"`
	CascadesTo      []string        `json:"cascades_to"`
	Defaults        map[string]bool `json:"defaults"`
	PositiveFixture *string         `json:"positive_fixture"`
	NegativeFixture *string         `json:"negative_fixture"`
	Kind            string          `json:"kind"`
}

Question is a single entry in the registry.

func (*Question) Invert

func (q *Question) Invert() bool

Invert reports whether this question's "yes" answer should DISABLE its rules rather than enable them. By convention, questions whose ID starts with "allow-" are inverted (yes = allow = rule off).

func (*Question) RulesetForYAML

func (q *Question) RulesetForYAML() string

RulesetForQuestion derives the YAML ruleset name for a question from its positive_fixture path. Fixture paths follow the tests/fixtures/positive/<ruleset>/<Rule>.kt convention. For parent questions with no fixture, returns an empty string — callers should fall back to an explicit mapping or skip the question.

type Registry

type Registry struct {
	SchemaVersion int        `json:"schemaVersion"`
	Description   string     `json:"description"`
	Questions     []Question `json:"questions"`
}

Registry is the parsed form of config/onboarding/controversial-rules.json.

func LoadRegistry

func LoadRegistry(path string) (*Registry, error)

LoadRegistry reads and parses the controversial-rules registry from disk. It validates schemaVersion, cascade references, and that every question has per-profile defaults for all four shipped profiles.

type RuleCount

type RuleCount struct {
	Name  string
	Count int
}

RuleCount is a (rule name, finding count) pair used by TopRules.

type ScanOptions

type ScanOptions struct {
	// KritBin is the path to the krit binary. Required.
	KritBin string
	// RepoRoot is the krit repository root (so ProfilePath can resolve
	// config/profiles/<name>.yml).
	RepoRoot string
	// Target is the directory to scan.
	Target string
}

ScanOptions configures a single krit invocation performed via the ScanProfile helper.

type ScanResult

type ScanResult struct {
	Profile  string                     `json:"profile"`
	Total    int                        `json:"total"`
	Fixable  int                        `json:"fixable"`
	ByRule   map[string]int             `json:"byRule"`
	Findings map[string][]FindingSample `json:"-"` // keyed by rule name; capped at 3 per rule
}

ScanResult holds the parsed JSON summary for a single profile scan. Only the fields the onboarding flow actually uses are modeled; the full krit JSON schema has many more keys.

func ScanProfile

func ScanProfile(ctx context.Context, opts ScanOptions, profile string) (*ScanResult, error)

ScanProfile invokes `krit --config <profile.yml> -f json <target>` and parses the resulting summary. Krit exits non-zero when findings exist; that is treated as success here — only JSON parse errors bubble up.

func ScanProfileWithProgress

func ScanProfileWithProgress(
	ctx context.Context,
	opts ScanOptions,
	profile string,
	progress func(ProgressEvent),
) (*ScanResult, error)

ScanProfileWithProgress is like ScanProfile but also emits ProgressEvents as `krit -v` advances through its verbose stages. Stderr is tailed line-by-line; each line matching a StrictStages prefix fires one callback. Progress events are delivered on the goroutine running this function — callers that need to hop onto another thread (e.g. a bubbletea message channel) should do that inside the callback.

The JSON summary is still parsed from stdout. A progress callback of nil degrades to a plain scan.

func (*ScanResult) TopRules

func (r *ScanResult) TopRules(n int) []RuleCount

TopRules returns the top-N rules by count for this scan, in descending count order, tie-broken by rule name.

type ThresholdOverride

type ThresholdOverride struct {
	Ruleset string
	Rule    string
	Field   string
	Value   int
}

ThresholdOverride is one ruleset+rule field value to merge into a profile. Used by the TUI threshold-slider phase to adjust numeric rule settings like LongMethod.allowedLines.

type WriteConfigOptions

type WriteConfigOptions struct {
	ProfileYAML        []byte              // contents of config/profiles/<name>.yml
	ProfileName        string              // profile display name for the header
	Overrides          []Override          // rule on/off overrides from the questionnaire
	ThresholdOverrides []ThresholdOverride // numeric threshold overrides from the slider phase
}

WriteConfigOptions configures a WriteConfig invocation.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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