Palabrita's Five-Day Lesson in Scope Reduction

Palabrita is a Wordle-style Android game that uses an on-device language model to write hints, and its five-day development record shows how a product discovers the boundary of a small model's reliable work. The important question is not whether the team removed features. It is why each reduction followed a particular failure, and why moving one responsibility out of the model ultimately solved more than making the prompt stricter. The case gives us a way to distinguish a mitigation that lowers an error rate from an architectural change that removes the error from the product path.

The original idea was attractive. A player would receive a fresh puzzle, and the model would generate the hidden word, its category, a difficulty value, and five hints in structured JSON. The design appeared to give the model a coherent creative role: invent the object, describe it, and calibrate the experience around it. But it also asked one small local model to satisfy several different kinds of requirement at once. It had to produce natural language, count letters, obey a language constraint, avoid repetition, select a real word, follow a schema, and complete enough output for the game to proceed.

That combination created a large failure surface before anyone had decided which part was genuinely valuable to players.

Day one: a plausible design meets the foreground deadline

The first implementation was built from specifications and a formal engine lifecycle. Its prompts carried assumptions familiar from cloud-model integrations, including an instruction to respond through a function that had never actually been implemented. The compact-model prompt asked for JSON, a word within a requested length range, a common noun in a specified language, five progressive hints, and no answer hidden inside a hint. The rules were clear to a human reader. Clarity of the rule, however, did not make it easy for a small model to execute.

The more immediate failure was time. The initial system generated seven puzzles synchronously during onboarding. The screen could remain blocked for one to three minutes, with progress stuck at zero and then jumping toward completion. The team responded by shipping static puzzles as assets and moving additional generation into a background system. That was a sensible reaction to the symptom: take long work away from the first screen so the user can start playing. It did not yet answer the deeper question of whether the model should be responsible for generating the puzzle's hard facts.

The model decision was also being made under practical limits. Qwen3 0.6B was added because its model file was about 614 MB and its stated memory tier was lower than Gemma 3 1B's. A model that could not be downloaded or tested quickly would not be a useful candidate in a five-day sprint, no matter how appealing its theoretical capabilities looked. This was the first lesson in the case: the evaluation boundary is shaped by storage, memory, and iteration time before quality comparisons become elaborate.

Day two: contain the output, then teach the prompt

Once the system was producing responses in real use, format failures became visible. Valid-looking JSON often arrived inside markdown code fences. Some responses included ordinary explanatory text around the JSON. In multilingual runs, the model sometimes translated the field names as well as the values, returning forms such as Portuguese equivalents of “word” and “hints.” Rare replacement characters could also make an otherwise usable response fail to parse.

The parser therefore evolved from a single expected format into a sequence of recovery attempts. It sanitized replacement characters, stripped code fences, tried normal JSON decoding, extracted a JSON object embedded in surrounding prose, and finally inspected the structure of the values rather than trusting the field names. An array of strings could be treated as hints, a short string without spaces as a candidate word, and an integer as a difficulty value. This was not a claim that malformed output was acceptable. It was a decision to recover safely when the response still contained enough information to be interpreted.

The prompt changed at the same time. The unused function-calling instruction was removed. The model was told to return only a JSON object, without markdown or explanation, and to keep the keys in English while writing values in the requested language. When word-length errors appeared, the prompt added a “CRITICAL” marker and told the model to count carefully. Later iterations added a concrete example of a word with six letters that would be rejected. The team was testing hypotheses about which instructions could alter the model's behavior.

Retries also became more informative. Instead of sending the same request again, the next prompt named the failure: the previous word had seven letters when five were requested, so the model needed a different five-letter word. Rejected words were tracked locally so that the next attempt would not simply repeat the same invalid answer. Chat sessions gave the model access to this immediate feedback, which improved some retries compared with blind repetition.

These changes were valuable, but their scope matters. A parser can recover a code fence; it cannot make an invalid word valid. A clearer retry can reduce a length mistake; it cannot guarantee that the next sampled word will be counted correctly. Prompt engineering and defensive parsing contained variability. They did not transfer ownership of the game's rules away from the model.

Day three: remove output that had no product job

The most consequential change on day three was not a new instruction. It was the removal of fields. Category was present in the schema but had no role in gameplay. Keeping it meant asking the model to generate and the application to validate a value that nobody used. Difficulty was represented as a one-to-five number, but the model returned arbitrary values and the game already expressed difficulty through word length. The number added a judgment problem without adding a user-facing decision.

The later hints had a similar problem. Hints four and five were often repetitive, too specific, or grammatically broken. The first three carried most of the useful progression. Reducing the target to three hints did not merely make the response shorter; it removed a low-quality tail from the experience. The schema moved from the initial seven-field design to a much smaller word-and-hints response, and the validator no longer insisted on an exact five-hint result.

The generation batches were simplified as well. Instead of switching among several word-length constraints within one context, a cycle used a single length range. A batch of four- or five-letter words did not require the model to change its counting rule after every response. This was a smaller change than the later curated list, but it followed the same principle: reduce the number of constraints the model must hold in play at one time.

The case is useful precisely because these reductions were not presented as inevitable from the beginning. The original design was a reasonable product hypothesis. Only contact with the outputs showed which fields were unused, which numerical distinctions were unstable, and which hints did not justify their cost. In hindsight, a team could have started with a smaller schema. During the sprint, the smaller schema became credible because the failures made the cost of the original one visible.

The errors were product failures, not only model failures

By this point the failure pattern had several distinct forms. The output could be structurally wrong even when its meaning looked clear. The word could have the wrong number of letters, be in the wrong language, repeat an earlier answer, or not exist in the intended vocabulary. A hint could reveal the answer. After several generations in one session, hints could become repetitive or less relevant. On the original synchronous path, the wait itself made onboarding unusable. Model selection added another layer: Gemma and Qwen did not respond to the same prompt in the same way, so every supported model implied a different reliability profile.

The reported rates show why “just prompt it harder” was not enough for this product. Early word-length violations occurred in roughly 30–50% of generations. Priority markers and concrete examples lowered the reported rate to about 10–15%, but that was still too high for a rule that determines whether a puzzle can be played. Code fences appeared in the majority of early calls and later fell to roughly 20–30% after explicit instructions. The structural parser was still needed for about 15–20% of successful Qwen3 parses. Context degradation was observed after about three generations with Qwen3 0.6B and about five to seven with Gemma 4 E2B, although those session counts came from development observation rather than a controlled experiment.

The numbers should not be treated as universal benchmarks. They describe one developer's rapid testing of particular models on particular hardware. Their value is diagnostic: different failures require different owners. A format variation may be recoverable at the parser boundary. An invalid hard fact is better prevented by deterministic data. A slow batch may need a different interaction design. A degrading session may need rotation. Treating all of them as prompt problems would conceal the causal differences.

stateDiagram-v2 [*] --> A state "Generate complete puzzle" as A state "Reduced schema" as B state "Word plus three hints" as C state "Curated word selection" as D state "Hint-only generation" as E state "Deterministic fallback" as F A --> B: Remove unused or unstable fields B --> C: Keep only useful puzzle content C --> D: Move hard word constraints to assets D --> E: Ask model for bounded creative text E --> F: Model response fails F --> E: Use model again when available

Day five: move the hard fact out of the model

The final pivot changed the ownership boundary. Words were selected from curated JSON assets organized by language and validated outside inference. The model received a known word and produced three short hints about it. That one decision removed several responsibilities at once. The model no longer had to count letters, invent a valid word, avoid a duplicate in the word list, select the right language's vocabulary, or ensure that the word existed.

What remained was not trivial, but it was better matched to the model. Writing a short hint about a supplied word is a bounded creative task. A hint can be imperfect without making the puzzle structurally invalid, provided it does not reveal the word or drift so badly that it becomes useless. The product could check those conditions and decide what to do when they failed. The hard lexical facts were now owned by curated data and validation; the variable prose was owned by generation.

The final prompt reflected that narrower job. It repeated the target language, prohibited the answer from appearing in a hint, required short sentences, and requested only a small hints object. The parser was specialized for that output and could also recognize hint-like keys in several languages or recover simple alternatives such as pipe-delimited and numbered hints. The system no longer needed a general puzzle interpreter because the product had stopped asking for a general puzzle response.

A deterministic fallback completed the design. If the model never produced a successful response, the game could still show generic hints in the relevant language, including a statement about the word's length. Those hints were less creative, but they preserved playability. This is the difference between an AI enhancement and an AI gate: the former improves the experience when it works; the latter makes the experience unavailable when it does not.

The same day brought a related architectural correction. A background-work abstraction had accumulated seven fixes because its persistent terminal states and process-survival assumptions did not fit an interactive operation whose engine state lived in memory and whose user watched progress. Replacing it with a coroutine-based use case removed the stale-state and initialization races that the added guardrails had been fighting. The lesson belongs to the same family as the prompt lessons: when an abstraction repeatedly resists the actual ownership and timing of a task, changing the boundary is often clearer than adding another guard.

What the case proves—and what it does not

Palabrita does not prove that small local models are incapable of all structured generation, nor that a curated list is always preferable to generation. It shows that, for this game and these tested models, hard lexical constraints were a poor responsibility for the model and bounded hint writing was a workable one. The failure percentages are development estimates, and the five-day, single-developer case cannot establish how another team, model, or device would behave.

Its stronger lesson is architectural. Prompt hardening can improve a behavior without guaranteeing it. Defensive parsing can recover a response without making its content true. Retries can turn feedback into a better next attempt without removing uncertainty. Session rotation can protect freshness while sacrificing some conversation context. A deterministic source and a fallback can eliminate or contain whole classes of failure.

Scope reduction, then, is not the same as giving up. It is the act of preserving the part of the experience that benefits from a model while moving non-negotiable responsibilities to components that can enforce them. The next question is how to apply that boundary deliberately: when a product should narrow a model's task, and when a broader system should keep more capabilities by controlling adaptation and execution around a shared foundation.

References