- Add 'verify:' to the parseSpeck dispatch condition so multi-line verify:
blocks reach the multi-line parser
- New parseVerifyColonBlock helper collects continuation lines using the
same break-condition list as the existing skip-loop, with depth-balanced
counter for (), [], {}
- Joins the parts with single spaces and returns a VerifyNode with a
well-formed temporalExpr
- Closes sscoble/speckl#54 — unblocks B2 PR stack (#47 + #48 already closed)
- Pre-existing LTL inner-identifier scoping bug remains in #55 (out of scope)
- Adds <meta name='author' content='Scott Scoble'> to landing page head
- Adds JSON-LD SoftwareSourceCode structured data with author, repo, license, keywords
- Updates hero subtitle to 'A Judgement Transport Utility by Scott Scoble'
- Updates OG description with 'Created by Scott Scoble'
- Updates OG site_name to 'Speckl by Scott Scoble'
- Keywords include 'scott-scoble' for name association
- Predicate pushdown now splits AND predicates and pushes each to
the correct side of the join (left table vs right table)
- FROM clause no longer duplicates tables that appear in JOINs
- Constant folding no longer mangles date literals (skips strings)
- Cost model: hash join uses build/probe costs, nested_loop uses
per-row probe cost instead of cartesian product
- Physical operator selection defaults to hash join for medium+ tables
- Two-pass SQL generation: collect join tables first, then emit FROM
- Demo shows 86% cost reduction on users/orders query, 25% on
departments/employees query from ekeric13/sql-query-optimization-guide
SQLOptimizer.speckdl: state machine spec for the optimizer pipeline
(Parsing → Rewriting → Planning → Generating → Done, plus Failed)
SQLOptimizer-runtime.ts: working TypeScript runtime that actually
optimizes SQL queries:
- Parses SELECT/FROM/JOIN/WHERE/GROUP BY/ORDER BY/LIMIT
- Builds plan trees with cardinality estimation
- Predicate pushdown (splits AND predicates per table)
- Constant folding (evaluates arithmetic in predicates)
- Column pruning
- Physical operator selection (hash/merge/nested_loop joins)
- Cost estimation per plan node
- SQL generation from optimized plan tree
Known issues:
- Predicate pushdown needs to split AND predicates per table
- FROM clause duplicate when both listed and joined
- Cost model needs tuning (hash join startup cost shouldn't exceed
nested loop savings for medium inputs)
Both files pass tsc --noEmit clean.
Parser fixes:
- Fixed multiline state/init block parsing: trailing commas no longer
treated as continuation markers, merging all state vars into one
- Added splitFields() function for comma-aware emit field splitting
that respects parenthesized groups and quoted strings
- Fixed interface block parsing: enum-style interfaces (name-only
values like 'Idle, Parsing, Parsed, ...') now correctly parsed as
methods instead of being skipped (methods.length was 0)
TypeScript generator fixes:
- String[] array type syntax now correctly maps to string[] in output
- collectIdents filters out names containing brackets
- Duplicate event interfaces deduplicated (implicit emit events
filtered when explicit events already declare them)
- join(X, Y) translates to X + Y (string concatenation) instead of
X.join(Y) which is wrong for string args
- now() generates new Date().toISOString() instead of Date.now()
to match String/string type annotations
- decisionLog type uses proper union: (A | B | C)[] instead of A[]
- String literals protected from this.-prefixing during identifier
replacement (fixes 'leader' inside quotes becoming 'this.leader')
- Removed debug console.log from enum generation
Both TigerBeetleLedger.speck and speckl-compile.speckdl now generate
TypeScript that passes tsc --noEmit with zero errors.
Closes#9
- Add braceDepth tracking to Map/Set type parsing for inline records
- Fix multiline state block joining to preserve type expressions
- Fix multiline object literal parsing in assignments
- Add smart set-difference for Maps vs Sets
- Fix bare notIn for Sets
- Add non-null assertion (!) to Map.get() calls
- Add implicit event interface generation from emit statements
- Protect object literals from this. prefix injection
The four design docs from the GP architecture research thread. These define Speckl's
general-purpose architecture: conversation → SIR → deterministic compile, with domain
plugins for state machines, data schemas, and workflows.
- sir-schema.md (48KB): Central IR data structure with ~20 interfaces, 8 domain element tables
- extraction-protocol.md (47KB): How conversation populates the SIR
- validation-engine.md (39KB): SIR validation before compilation
- compilation-layer.md (43KB): How validated SIRs compile to deterministic artifacts
Phase strategy: build GP core now, ship state machines first, layer data schemas
and workflows on the same architecture (no rewrites).
- Declare speckl_len_Int helper for list/sequence lengths
- Fix infix = inside => bodies (final pass regex)
- Handle prime notation: currentTerm' → currentTerm_post
- Handle length() calls: length(log) → (speckl_len_Int log)
- Handle min(): (ite (<= a b) a b) instead of unsupported 'min'
- Handle subtraction: i-1 → (- i 1)
- Declare _post state vars when invariants compare pre/post
- Added 3 BMC verify blocks to KRaft spec (TermMonotonicity, CommitSafety, LeaderCompleteness)
- Z3 returns clean sat with zero errors for all specs
- All 37 tests pass
- Sanitize sort names: Option(NodeId) → Option_NodeId
- Emit action transition predicates only for verify/BMC mode
- Handle -> implication operator in translateExpr
- Fleshed out KRaft invariant bodies (4/6 now have real expressions)
- ToggleSwitch: clean sat with model
- TigerBeetleLedger: sat with model
- KafkaKRaft: sat with model
- All 37 tests pass
- x in someSet → someSet.has(x)
- x in someMap.keys → someMap.has(x)
- x notIn someMap.keys → !someMap.has(x)
- m[k] → m.get(k) for Map state variables
- s.size() → s.size (property, not method)
- Track Map-typed state variables via mapVarNames for accurate .get() translation
- Thread mapVarNames through emitAction and rewriteExpr
All 41 tests passing. TigerBeetleLedger now generates correct TypeScript
with .get() for Map access and .has() for Set membership.
Part of M0.1: Fix TS generator bugs (speckl#18)
- Deleted compiler/src/generators/wasm.ts (472 lines)
- Deleted compiler/src/runtime/wasm-host.ts (165 lines)
- Deleted compiler/__tests__/e2e-wasm.test.ts (203 lines)
- Removed WASM import and generation call from compiler/src/index.ts
- Removed WASMGenerated from TS generator state enum literals
- Removed WASM codegen tests from parser.test.ts
- All 37 tests passing
The WASM generator was architecturally wrong: every Map/Set operation
crossed the WASM→JS boundary, creating 45-55% overhead for a module
that doesn't compute anything. WASM will be a byproduct of the Rust
backend via wasm-pack instead (M2.1).
Closes#19
- Add 'precondition' and 'postcondition' ActionStatement types
- Parse precondition: expr and postcondition: expr in action bodies
- Support 'or' continuation lines for multi-clause pre/postconditions
- TigerBeetleLedger now parses 21 preconditions + 24 postconditions
(previously all dropped, resulting in empty action bodies)
- All 41 existing tests pass
Addresses marcus/engineering#9 (parser gap)
Does NOT wire into generators yet — TypeScript/WASM output
will be empty for pre/postconditions. Generator wiring
is follow-up work.
blog-01: Spec-Code Drift Is a Provenance Problem (~1,500 words)
blog-02: Designing SpeckDL (~1,800 words)
blog-03: How Speckl's Compiler Works (~1,900 words)
blog-04: Embedded Provenance (~1,200 words)
blog-05: Consensus Protocols in SpeckDL (~1,300 words)
publishing-toolkit: Dev.to/Medium/Reddit/HN formatting + objection prep (~3,500 words)
Total Phase 2 pipeline: ~11,200 words across 6 files.
Previously existed only in workspace copy (posts 1/3/toolkit) or
as untracked files (posts 2/4/5). All now committed to Forgejo.
Runs full pipeline (parse → generate → TS check) on all 11 examples.
Produces structured pass/fail report with artifact counts.
Supports --quick (skip WAT) and --verbose (show errors).
Current state: 5/11 clean (AccessControl, DrugDosageCalculator,
PaymentService, RetryHandler, TemperatureMonitor). 6/11 have TS errors
from known issues (engineering#9: precondition drops, record type refs,
spurious this.state prefix).