mcpbeat Sign in

Unknown Not A Valid Score Agent Skill

A parse-failure fallback must be a sentinel outside the legal domain, or expressed in the type — never a value the success path can also produce. Expose "not known" as its own question. Use when a field means two different things depending on where it came from, when a placeholder reaches the screen, or when a consumer cannot tell absent from measured.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
100
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/maxrave-dev/kotlin-footguns --skill unknown-not-a-valid-score

The instruction itself

3 sections, as written by the author

"Could not parse" is not a value

Every fallback in a parser answers a question the caller will ask later. ?: 0 answers "how many"

with a number that means both *zero of them* and *the parser gave up*. ?: "" answers "which one"

with a string that means both *none* and *unreadable*. The two meanings then travel together

through every consumer, and each consumer has to guess — differently.

There are only three honest fallbacks:

  • A sentinel the success path can never produce — a negative where the domain is

non-negative, MAX_VALUE where the domain is a real measurement.

  • Absence in the typenull, or an Unknown case in a sealed hierarchy.
  • No fallback at all — fail the parse and let the caller decide.

Absence in the type is the one that cannot be misread, because the compiler makes every consumer

say what it wants to happen. Reach for a numeric sentinel only where a nullable type is genuinely

not available — a database column, a fixed-width protocol field, an interface you do not own.

Traps

A sentinel that is legal in the *type* is not a sentinel. -1L is not a valid position, so

parking a position field at -1L looks safe — and it is safe only while every reader agrees. The

source project's own record describes what happened once that agreement broke: one branch wrote

-1L to mean "no position", a second ignored negative values entirely and so never overwrote it, a

third restored the neighbouring total without touching it, and the single formatter rendered any

negative as a placeholder. The screen showed a correct total next to a placeholder that never

cleared, because nothing was guaranteed to write again. The fix was to stop minting the sentinel *on

the path with no guaranteed follow-up write*, parking the field there at a real in-domain value. The

initialiser elsewhere still mints it, which is the point: a sentinel is worth exactly as much as the

guarantee that something overwrites it.

The formatter itself was right — reserving the whole negative half is exactly this pattern:

// adapted — names generalized
fun formatDuration(duration: Long): String {
    if (duration < 0L) return notAvailableLabel   // negative is reserved, never a real duration
    …
}

The lesson is not "don't use sentinels", it is **one owner decides the sentinel and every reader

goes through the same accessor** — a formatter that renders it, a predicate that tests it. A

sentinel with three independent readers has three definitions.

A fallback inside the legal range makes every consumer carry the convention. A duration parser

that returns 0 for unparseable input is documented and deliberate, and it still means every

downstream user must write takeIf { it > 0 } before sending, storing, or displaying it — forever,

including the ones added next year. Count the guards: if the same > 0 appears at more than two

call sites, the fallback is in the wrong place.

Two questions need two functions. "Is this a video?" and "did the source say what this is?" are

different, and a boolean that folds an unknown into false answers only the first while looking

like it answers both. Expose the second explicitly, and say in the signature which way an unknown

falls, because reasonable implementations resolve it in opposite directions:

// adapted — names generalized
fun isVideo(kind: String?): Boolean = normalize(kind)?.let { it != AUDIO } == true  // unknown → false
fun isAudio(kind: String?): Boolean = normalize(kind) == AUDIO                      // unknown → false
fun isKnown(kind: String?): Boolean = normalize(kind) != null                       // ask this first

Both predicates return false for an unknown, which is only coherent because isKnown exists next

to them. Without it, !isVideo(x) reads as "audio" and quietly is not.

Normalize before comparing, because stored rows outlive the code that wrote them. A column

populated by earlier builds holds whatever those builds invented — labels from a different

vocabulary, and in one recorded case a numeric count written into a label column. Comparing those

raw classifies all of them as *something*. Run every value through one normalizer that keeps only

values the source itself can emit and returns null for everything else, then compare the

normalized result. null from that normalizer means "nobody has ever told us", which is exactly

what you want it to mean.

The same rule has a UI-colour form, and the stand-in is visible. A colour derived from an image

— a page tint, an accent, a glow — is *not resolved yet* for the first frames, and the tempting

default is a theme colour: it composes and looks plausible. Same defect — a real value the success

path also produces. It flashes as the wrong tone on load, and while nothing is selected it paints a

confident tint for something that does not exist. Two honest answers, used together:

// adapted — Unspecified is the "not resolved" sentinel; null is "nothing is selected"
val state = rememberDominantColorState(defaultColor = Color.Unspecified, …)
fun rememberGlowTint(imageUrl: String?): Color? =
    if (imageUrl == null) null else state.color.takeIf { it.isSpecified }

The consumer must then treat null as *draw nothing* rather than substituting its own default: the

gradient collapses into the page colour, so the layer is invisible until a tone arrives. A default

at the consumer re-creates the stand-in one layer down, where it is harder to find.

Never invent a placeholder to satisfy a non-null field. "Unknown Artist", "0:00", 1970-01-01

are all values that will be persisted, matched against, sorted, and eventually shown. If the model

demands a value the payload does not contain, the model is wrong — make the field nullable or drop

the record, and see structural-defensive-parsing for counting what you drop rather than losing it

silently.

Mint local failure codes outside the remote range. When a call fails before reaching the

service there is no service code, and reusing 0 or any legal code makes a local failure

indistinguishable from a remote verdict. A negative code, where the service's own are positive,

keeps the two apart in one comparison — see api-ok-but-ignored.

Verifying it

List the most common fallbacks that land inside the success domain and justify each one — these two

patterns catch the numeric and the invented-placeholder shapes, not every producer of them:

grep -rnE "to(Int|Long|Float|Double)OrNull\(\) \?: (0|0L|0f|-1|-1L)\b" --include="*.kt" . | grep -v "/build/"
grep -rniE "\?: \"(unknown|n/?a|untitled|none|no name)" --include="*.kt" . | grep -v "/build/"

For each hit, ask the one question that settles it: **can the success path produce this same

value?** If yes, the fallback is a defect however well documented. The second command finds invented

placeholders specifically — expect it to hit display strings never meant to be persisted, and check

where each one ends up. Widening either to a bare ?: "" or ?: emptyList() does not scale: on any

sizeable codebase it returns hundreds of lines, most legitimate. Narrow to the boundary you audit.

Then confirm the unknown state is reachable by callers:

grep -rnE "fun isKnown|data object Unknown|-> Unknown|UNKNOWN" --include="*.kt" . | grep -v "/build/"

For the colour form, list every derived-colour default beside every sentinel check — a theme role in

the first list with no counterpart in the second is a stand-in nobody can tell from a real value:

grep -rnE "default(On)?Color *=|fallbackColor *=" --include="*.kt" . | grep -v "/build/"
grep -rn "isSpecified\|Color.Unspecified" --include="*.kt" . | grep -v "/build/"

Finally, feed the parser a payload with the field deleted, then one with the field present but

garbage. Those two must be distinguishable at every layer — parser, model, storage, screen. If they

render identically anywhere, the sentinel was lost at that layer and everything below it is guessing.

How to use it

Copy the folder

Take maxrave-dev/unknown-not-a-valid-score from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.