{"record":{"id":"871e58d23d40414f","repo":"JuliusBrussee/caveman","slug":"unknown-learn-subcommand-s","errorCode":null,"errorMessage":"unknown learn subcommand: %s","messagePattern":"unknown learn subcommand: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/cmd/caveman-proxy/main.go","lineNumber":603,"sourceCode":"\t\t\tfatalJSON(logger, err)\n\t\t}\n\t\tif hasArg(args, \"--json\") {\n\t\t\tprintJSON(plan)\n\t\t\treturn\n\t\t}\n\t\tprintJSON(map[string]any{\"report\": out, \"sidecar\": sidecar, \"cave_score\": plan.CaveScore.Score, \"basis\": plan.Basis, \"sinks\": len(plan.Sinks)})\n\tcase \"apply\":\n\t\tsinkID := firstPositional(args)\n\t\tif sinkID == \"\" {\n\t\t\tfatalJSON(logger, fmt.Errorf(\"usage: caveman-proxy learn apply <sink_id> [--dry-run]\"))\n\t\t}\n\t\tplan, err := spend.BuildLearnPlan(cwd, sources, since)\n\t\tif err != nil {\n\t\t\tfatalJSON(logger, err)\n\t\t}\n\t\tapplyLearnSink(logger, home, sinkID, hasArg(args, \"--dry-run\"), plan)\n\tdefault:\n\t\tfatalJSON(logger, fmt.Errorf(\"unknown learn subcommand: %s\", sub))\n\t}\n}\n\nfunc learnRetroOptions(args []string) store.RetroOptions {\n\tbehaviorBudget, _ := strconv.Atoi(argFlag(args, \"--behavior-budget-ms\", \"0\"))\n\tretroBudget, _ := strconv.Atoi(argFlag(args, \"--retro-budget-ms\", \"0\"))\n\treturn store.RetroOptions{\n\t\tEnabled:          hasArg(args, \"--retro\"),\n\t\tBehaviorBudgetMS: behaviorBudget,\n\t\tBudgetMS:         retroBudget,\n\t}\n}\n\n// applyLearnSink materializes a candidate edit for a reducible sink under\n// ~/.caveman/candidates/ and returns it. It never edits user config files itself —\n// the analyzer is read-only; the consent-gated editing skill performs real edits.\nfunc applyLearnSink(logger *slog.Logger, home, sinkID string, dryRun bool, plan store.LearnPlan) {\n\tvar target *store.Sink","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/cmd/caveman-proxy/main.go#L585-L621","documentation":"The `learn` command dispatcher hit a subcommand token outside its known set (scan is the default, plus apply). fatalJSON echoes the token and exits 1. Note learn defaults to scan when run with no args, so this only fires on an explicit unknown token.","triggerScenarios":"`caveman-proxy learn report`, `learn list`, or a typo like `learn aply`. The switch only has scan/apply arms.","commonSituations":"Expecting report verbs under learn (the scan output is the report); scripts iterating assumed verbs; muscle-memory from other agent CLIs.","solutions":["Use scan (or no subcommand) to analyze, apply <sink_id> to materialize a candidate","Check help output for the exact verb set of your binary","For the cave-score report, read the scan JSON rather than a separate verb"],"exampleFix":"# before\ncaveman-proxy learn report\n\n# after\ncaveman-proxy learn scan --json","handlingStrategy":"validation","validationCode":"case \"$SUB\" in \"\"|scan|apply) caveman-proxy learn $SUB \"$@\";; *) echo \"unknown learn subcommand: $SUB\" >&2;; esac","typeGuard":null,"tryCatchPattern":null,"preventionTips":["learn defaults to scan with no args — use that for analysis","apply <sink_id> is the only other verb; reports come from scan output","Verify the verb set against your binary version before scripting"],"tags":["cli","usage-error","exit-1"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}