Agentic Coding Has No Floor
Opinions Rasmus Ros 9 min readVibe coding is what agentic coding decays into when you're tired or four hours in. The structural fix has to come from the harness vendors, not from another instruction file.
Eignex builds COMBO, a Kotlin library for optimizing agents under hard constraints. Pick the model, temperature, prompt style, and tools per request. Learn from how each run scored. Never violate the rules you declared.
View COMBO on GitHubYou declare a decision space: typed variables (model, temperature, prompt style, tools) plus the rules they have to obey.
No top-tier model on the free plan. Code execution requires a sandbox tool. Casual tasks stay under a token cap.
Every configuration COMBO hands back is one that satisfies every rule, by construction.
Use it two ways. Embed the Kotlin multiplatform library directly in your service. Or deploy COMBO as its own pod and call into it from any language through the SDKs. Same decision space, same loop, same guarantees.
Variables and constraints live in a single Kotlin class. Each variable carries a type (boolean, integer, float, nominal, or a multi-select set) and each constraint is a plain logical expression over those variables and the per-request context. The compiler hands the result to a constraint solver that knows how to sample feasible configurations from it. Every choose call returns one of those configurations.
class AgentPolicy : DecisionSpace() {
val taskType by contextNominal("coding", "writing", "research", "casual")
val userTier by contextNominal("free", "pro", "enterprise")
val model by nominal("haiku", "sonnet", "opus")
val temperature by floatVar(min = 0.0, max = 1.0, buckets = 16)
val promptStyle by nominal("terse", "detailed", "chainOfThought")
val tools by multiple("web_search", "code_exec", "file_read", "bash")
val freeTierCantUseOpus by constraint { (userTier eq "free") implies (model ne "opus") }
val codeExecNeedsBash by constraint { tools.contains("code_exec") implies tools.contains("bash") }
val casualKeepsItCheap by constraint { (taskType eq "casual") implies (model ne "opus") }
}
name: AgentPolicy
context:
taskType: { type: nominal, labels: [coding, writing, research, casual] }
userTier: { type: nominal, labels: [free, pro, enterprise] }
variables:
model: { type: nominal, labels: [haiku, sonnet, opus] }
temperature: { type: float, min: 0.0, max: 1.0, buckets: 16 }
promptStyle: { type: nominal, labels: [terse, detailed, chainOfThought] }
tools: { type: multiple, labels: [web_search, code_exec, file_read, bash] }
constraints:
freeTierCantUseOpus:
type: imp
left: { type: nomeq, name: userTier, label: free }
right: { type: not, child: { type: nomeq, name: model, label: opus } }
codeExecNeedsBash:
type: imp
left: { type: ref, name: tools.code_exec }
right: { type: ref, name: tools.bash }
casualKeepsItCheap:
type: imp
left: { type: nomeq, name: taskType, label: casual }
right: { type: not, child: { type: nomeq, name: model, label: opus } }
COMBO covers two regimes from one decision space. Use it online for high-volume traffic where every request is a new draw and you care about cumulative reward over millions of decisions. Use it offline for expensive sweeps where each evaluation costs minutes or money and you have a budget of tens to hundreds of trials. Same schema, same rules, a different optimizer plugged in behind the loop.
Either way the call shape is the same. Ask for a configuration, hand it to the request that needed it, then wait for the outcome. A null return is the rare honest answer that no configuration satisfies the rules, not a quiet fallback.
val choice = combo.choose() ?: return // null only if no feasible config exists
serve(choice) // your code: run the request with this configuration
combo.update(choice, observe()) // score the run, learn for next time
# null exit only if no feasible config exists
choice=$(curl -fsS -X POST "$COMBO_URL/v1/choose" \
-H 'content-type: application/json' \
-d '{"context": {"taskType": "coding", "userTier": "pro"}}') || exit
# your code: run the request with this configuration
serve "$choice"
# score the run, learn for next time
curl -fsS -X POST "$COMBO_URL/v1/update" \
-H 'content-type: application/json' \
-d "{\"choice\": $choice, \"reward\": $(observe)}"
Every observed outcome feeds back into the model. Updates are safe to apply from many threads at once, with the locking strategy picked per statistic so hot paths stay fast. Choose returns in milliseconds whether embedded or behind an SDK call.
Each pod learns on its own and ships what it learned to the others. Run as many copies as you need. They converge to the same answer as a single instance would, with nothing central to coordinate them on the request path.
Vibe coding is what agentic coding decays into when you're tired or four hours in. The structural fix has to come from the harness vendors, not from another instruction file.
Three months into the Eignex rewrite, the libraries finally share one config shape that doubles as a YAML wire format. A checkpoint on what changed in each repo.
Three attempts at typed schemas in Kotlin: an imperative builder, a type-encoded product, and the property-delegate design I ended up shipping as skema.
AI plus a feed isn't a new medium, it's the same engagement loop with cheaper supply. The objective the loop optimizes for is a choice, not a law of physics.
Sometimes 80 characters of URL is all you get, and JSON won't survive the trip. kencode squeezes structured state through it, with the schema written as a plain Kotlin data class.
What I'm actually trying to build: a continuous optimizer that learns from a live stream, fits a probabilistic model, and leans on an SMT solver to stay inside hard constraints.
A quick who-am-I and what-is-this. PhD in continuous optimization, left academia, now building Eignex in the open.