Spec to ship, in that order.
TinyLang was not coded and then documented. Each subsystem was specified, designed and broken into tasks with Kiro before a line of it was written. Here is how it came together.
Requirements, design and tasks first
Every subsystem starts as a spec in .kiro/specs/: user stories with acceptance criteria, a design doc with the data flow and the operator precedence table, then a task list with checkboxes. The compiler, VM, debugger, toolchain, Web IDE and test framework each got the full treatment before implementation.
Lexer, parser, tree-walk interpreter
The interpreter is the reference implementation, built test-first. Six data types, classes with inheritance, closures, pattern matching, f-strings, ranges and try/catch. Error messages are written for beginners: they name what went wrong and point at the fix.
Bytecode, optimized, on a stack machine
A 63-opcode compiler with constant folding, dead-code elimination and a peephole pass, feeding a stack VM with call frames, upvalues captured by reference and a try/catch handler stack. Then the hard question: does it agree with the interpreter?
17 commands around the language
A stepping debugger, a formatter that refuses to change meaning, a linter with auto-fix, a built-in test runner, a profiler, an AST viewer, a WebAssembly emitter and a 480KB browser IDE. One CLI, no external services.
Cross-validate, then trust
A differential suite runs the same programs through both backends and asserts byte-identical output. It found six real VM bugs that a wall of hand-written tests had missed. Correctness is measured by agreement between engines, not by the size of the test count.