{"record":{"id":"68f90181ffe8d718","repo":"JuliusBrussee/caveman","slug":"sink-q-is-not-a-bounded-recurring-context-or-reducible","errorCode":null,"errorMessage":"sink %q is not a bounded recurring-context or reducible config sink","messagePattern":"sink %q is not a bounded recurring-context or reducible config sink","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/store/learn_simulate.go","lineNumber":107,"sourceCode":"\t\t\towners[key] = id\n\t\t}\n\t}\n\n\tvar total int64\n\thasSummable := false\n\ttotalValid := true\n\tfor _, id := range ids {\n\t\tsink := byID[id]\n\t\trow := LearnSimulationRow{SinkID: id}\n\t\tif strings.HasPrefix(id, \"recurring_context:repaste:\") {\n\t\t\trow.Method = \"occurrence_sum\"\n\t\t\trow.Basis = \"bytes4_estimate\"\n\t\t\trow.Caveats = []string{\"Arithmetic retains one largest observed occurrence and charges one standard pointer for every observed occurrence; recall cost is unavailable here, so real apply still requires the net-token-negative gate.\"}\n\t\t} else if isSimulatableConfigSink(sink) {\n\t\t\trow.Method = \"per_turn_times_observed_turns\"\n\t\t\trow.Caveats = []string{fmt.Sprintf(\"Would not have been established across the %d provider-counted turns actually scanned; no future turn is included.\", observedTurns)}\n\t\t} else {\n\t\t\treturn LearnSimulation{}, fmt.Errorf(\"sink %q is not a bounded recurring-context or reducible config sink\", id)\n\t\t}\n\n\t\tif key := simulationOverlapKey(sink); key != \"\" && owners[key] != id {\n\t\t\trow.Caveats = append(row.Caveats, fmt.Sprintf(\"Excluded from arithmetic because it overlaps %s on %s; counting both would double-count.\", owners[key], key))\n\t\t\tout.Rows = append(out.Rows, row)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar skipped int64\n\t\tif row.Method == \"occurrence_sum\" {\n\t\t\ttotalTokens, totalOK := int64Evidence(sink.Evidence[\"occurrence_tokens_total\"])\n\t\t\tlargest, largestOK := int64Evidence(sink.Evidence[\"block_tokens\"])\n\t\t\toccurrences, occurrencesOK := int64Evidence(sink.Evidence[\"occurrences_total\"])\n\t\t\tif !totalOK || !largestOK || !occurrencesOK || occurrences <= 0 {\n\t\t\t\trow.Caveats = append(row.Caveats, \"Required occurrence totals were unavailable; no arithmetic emitted.\")\n\t\t\t\tout.Rows = append(out.Rows, row)\n\t\t\t\tcontinue\n\t\t\t}","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/internal/store/learn_simulate.go#L89-L125","documentation":"simulateLearnPlan can only compute arithmetic for sinks that are either recurring-context sinks with a bounded bytes4 estimate or reducible config sinks (isSimulatableConfigSink). Any other sink kind hits the final else branch and errors out, because no bounded estimation method exists for it. This is a deliberate fail-closed guard against extrapolating unbounded context growth.","triggerScenarios":"Passing a sink id to BuildLearnSimulationFiltered whose sink kind is neither a bounded recurring-context sink (bytes4_estimate path) nor a simulatable config sink, e.g. a recall-style or unbounded-growth sink.","commonSituations":"Selecting all plan sinks in a bulk simulation; a new sink kind was added to the plan but simulation support was not; confusing an establish-only sink with a simulatable one.","solutions":["Restrict the simulation selection to sinks the plan marks as recurring-context or reducible config sinks","Check the plan row's method/basis to see which sinks support arithmetic before selecting them","If the sink should be simulatable, ensure the scan classified it as bounded (or add support upstream)"],"exampleFix":"// before\nstore.BuildLearnSimulationFiltered(plan, allSinkIDs)\n// after\nvar simulatable []string\nfor _, id := range allSinkIDs {\n    if isSimulatable(plan.Sinks[id]) {\n        simulatable = append(simulatable, id)\n    }\n}\nstore.BuildLearnSimulationFiltered(plan, simulatable)","handlingStrategy":"type-guard","validationCode":"func isSimulatable(s PlanSink) bool {\n    return s.Kind == \"recurring-context\" || isSimulatableConfigSink(s)\n}","typeGuard":"func canSimulate(id string, byID map[string]PlanSink) bool {\n    s, ok := byID[id]\n    if !ok { return false }\n    return s.Basis == \"bytes4_estimate\" || isSimulatableConfigSink(s)\n}","tryCatchPattern":null,"preventionTips":["Only select sinks whose plan row shows a bounded basis (bytes4_estimate) or a config-reducible method","Check the plan's method/caveat fields before bulk-selecting all sinks","Treat establish-only or unbounded sinks as display-only in tooling","Keep sink-kind classification and simulation support in sync when adding new kinds"],"tags":["learn-simulate","unsupported-operation","cli"],"backgroundTag":"unsupported-operation","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}