agentsclimarketplace

No invalid states

Skill Vivswan/skills/skills/no-invalid-states

Installable agent skills and plugin-ready workflows for coding agents

Install
npx -y skills add Vivswan/skills --skill no-invalid-states

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Refactor code so invariants are enforced by the type system instead of repeated runtime checks. Parse and validate at trust boundaries, then make invalid internal states impossible to construct. Use when reviewing or refactoring code with boolean lifecycle flags, optional fields that must appear together, "must call X before Y" comments, repeated validation of the same value, or field combinations that should be contradictory. Also triggers on requests like "make illegal states unrepresentable", "parse, don't validate", or "strengthen these types".

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

9.3 KB, as published. Nobody here has run it

No Invalid States

Review and refactor a codebase so that correctness constraints are carried by the strongest mechanism the language provides, not by scattered defensive checks.

The core principle:

Validate at boundaries, convert into stronger representations, and make invalid internal states difficult or impossible to construct.

This is the "parse, don't validate" discipline. It does not mean deleting runtime validation: checks on external, dynamic, or untrusted data stay. The goal is that each fact is checked once, at the edge, and then encoded in a representation the rest of the program can trust.

When to Apply

Use this skill when the code shows any of these signals:

  • repeated runtime checks of the same condition (if !initialized, if value is None, assert ready)
  • booleans that encode lifecycle or mutually exclusive states
  • several optional fields that must always appear or be absent together
  • methods that are only valid after another method has been called
  • comments such as "must call X before Y" or "only valid when connected"
  • functions re-validating data that a caller already validated
  • structs, classes, or interfaces that permit contradictory field combinations
  • incorrect operation ordering that could be rejected statically
  • casts, non-null assertions, or type ignores used to quiet the checker

Do not use this skill to add abstraction to code that is already simple, or in place of a bug hunt; it improves representation, not behavior.

Workflow

1. Find candidate invariants

Search for the telltale patterns:

if !initialized        if value is None         state === ...
if !connected          if value === undefined   is_ready / is_valid
assert ready           has_loaded               instanceof / isinstance chains

Also inspect:

  • boolean lifecycle flags and state enums surrounded by unrelated optionals
  • constructors that produce partially valid objects
  • public setters that can break an invariant after construction
  • methods that fail only because another method was not called first
  • unchecked casts or assertions that exist to convince the type checker

For each candidate, write the invariant as one sentence. For example: "a connection may only send data after it has successfully connected." If you cannot state the invariant, you cannot encode it; skip it.

2. Classify each invariant: static or dynamic

Static, internal invariants belong in the program's structure. Examples: initialized vs uninitialized, authenticated vs unauthenticated, parsed vs raw, validated ID vs arbitrary string, mutually exclusive states.

Dynamic, external conditions keep their runtime checks. Types cannot prove that an HTTP request succeeded, a file still exists, a token has not expired, user input is well-formed, or a service is reachable.

The target shape is a one-way pipeline:

untrusted or raw value
  -> validate / parse once, at the boundary
  -> strong internal representation
  -> trusted internal APIs (no re-checking)

3. Choose the strongest idiomatic representation

Pick by situation, not by favorite mechanism:

SituationRepresentation
State changes at runtime and code inspects which state it is inSum type: enum with data, discriminated union, sealed class
Available operations depend on state; wrong ordering should fail to compileTypestate: state-parameterized types, consuming transitions
A primitive has special meaning or is validated once then trustedNewtype or branded type behind a smart constructor
Two states carry meaningfully different data; optionals encode which oneSeparate types per state
An invariant spans construction (fields must agree)Private fields, factory or constructor that validates, immutability
The invariant is really about the data storeSchema constraints: NOT NULL, CHECK, foreign keys, unique

Detailed, idiomatic guidance with code examples lives in the per-language references:

If the language at hand is not covered, map the situation table onto whatever the language offers: sealed hierarchies, smart constructors, immutability, and module privacy exist almost everywhere in some form.

4. Refactor

For each accepted candidate, work through this checklist:

  1. State the invariant and the invalid state currently representable.
  2. Introduce the stronger representation.
  3. Put validation at the boundary: one parser or factory that turns raw data into the strong type, with a real error path.
  4. Change internal APIs to accept the strong type, so downstream code cannot receive an unvalidated value.
  5. Remove checks the new representation makes logically impossible.
  6. Keep, and briefly justify, checks that remain because they are dynamic.

Rules while refactoring:

  • Make illegal construction hard: private fields, validating constructors, factory functions, state-transition functions, frozen or readonly data.
  • Validate once. validate -> use, validate -> use becomes parse once -> use, use, use.
  • Never silence the type checker with broad casts, any, non-null assertions, or ignore comments to force a refactor through.
  • Preserve existing behavior unless you have confirmed a bug; if you find one, report it separately rather than fixing it silently.
  • Respect public API compatibility where practical; strengthen internals first, then widen outward.
  • Make the smallest architectural change that removes the invalid state. Do not convert every boolean into a state machine. A refactor is worthwhile only when it eliminates repeated checks, prevents wrong ordering, removes impossible field combinations, or clarifies the API contract. Readability beats type-system cleverness.

5. Test and verify

Update or add tests for valid state transitions, rejected boundary input, exhaustive state handling, and behavior preservation. Add compile-fail or type-level tests only if the project already has a mechanism for them.

Then run the repository's own tooling. Inspect the project configuration (package.json scripts, Makefile, Cargo.toml, pyproject.toml, CI workflows) rather than assuming command names. Typical gates:

  • Rust: cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace
  • TypeScript: the project's typecheck, lint, test, and build scripts
  • Python: the configured ruff / pyright / mypy / pytest setup
  • anything else: whatever the repo's CI runs

Do not introduce a new type checker or dependency just for this skill unless that is genuinely appropriate for the repository.

6. Report

When asked to modify code, implement the changes rather than describing them. At completion, report:

  • Invariants improved: what was previously enforced by convention or runtime checks
  • Invalid states removed: what contradictory data or wrong ordering was previously possible
  • New representation: which mechanism now carries each invariant (for example: Rust typestate, TypeScript discriminated union, Python NewType)
  • Runtime validation retained: which checks stay because they depend on external or dynamic information
  • Files changed and why
  • Verification: the exact formatter, type-checker, lint, build, and test commands run, and whether they passed

Review Criteria

Skills that run code reviews (such as rubber-duck-review) expand this section into their reviewer prompt when this skill is installed. Ask the reviewer to flag:

  • invariants enforced only by runtime checks or convention: boolean lifecycle flags, optional fields that must appear or be absent together, "must call X before Y" ordering enforced at runtime, repeated validation of already-validated values, and field combinations that should be impossible to represent
  • for each finding, the stronger representation (sum type, newtype, typestate, validating constructor) that would remove the invalid state

Triage the resulting findings with the workflow above.

Guiding Principle

Before adding another defensive check, ask: can this invalid state be removed from the program's representation instead? Use the strongest idiomatic mechanism the language offers, and keep the design simpler, not more complicated, than the problem itself.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.