Sigil Debugging
Sigil debugging is built around machine-readable compiler and runtime surfaces. The compiler, runner, and test harness expose the state an LLM or a human actually needs instead of assuming a traditional IDE debugger.
Examples below use the installed sigil CLI. When working from a source build, the equivalent form is:
cargo run -q -p sigil-cli --no-default-features --
The Model
Sigil debugging and review are split into five surfaces:
inspect: what the compiler or runtime setup believesreview: what changed semantically across a git-selected patchrun: what one program execution didtest: what one test suite execution diddebug: replay-backed stepping over one recorded run or one recorded test
The important design choices are:
- JSON is the primary debugging surface
- replay is the determinism backbone
- runtime failures prefer exact Sigil expression blame when available
- stepping is replay-backed rather than attached to a live debugger process
- watches, traces, and breakpoints all use compact machine-oriented summaries
Choose The Right Command
| Question | Command | | --- | --- | | Did the compiler reject the source shape? | sigil inspect validate | | What top-level types did the checker solve? | sigil inspect types | | Which proof surfaces and branch gates exist here? | sigil inspect proof | | What changed semantically in this PR or staged patch? | sigil review ... | | What runtime world will this env or standalone file use? | sigil inspect world | | What TypeScript did this compile to? | sigil inspect codegen | | Where did one run fail? | sigil run --json | | How did execution flow? | sigil run --json --trace | | Which exact expression failed? | sigil run --json and inspect error.details.exception.sigilExpression | | Can I reproduce the same run exactly? | sigil run --json --record then --replay | | What did one test suite do? | sigil test ... | | Can I replay one failing test? | sigil test --record then sigil debug test start --replay | | Can I step through a recorded execution? | sigil debug run ... or sigil debug test ... |
Review Surface
sigil review
Use this when you need a human-reviewable semantic diff for Sigil code rather than a raw textual patch.
Examples:
sigil review --staged
sigil review --base origin/main --head HEAD
sigil review -- origin/main...HEAD -- projects/todo-app
sigil review --json -- origin/main...HEAD
sigil review --llm -- origin/main...HEAD | claude
Current design:
- git selects which files changed
- Sigil computes the typed/canonical facts for those snapshots
- the default output is a compact human-readable report
--jsonemits the structured machine envelope--llmemits grounded prompt text for a separate model
Current report focus:
- declaration additions/removals/modifications
- signature, contract, termination, type/refinement, and effect changes
- new or changed trust surfaces such as
extern - changed test files plus review-time test evidence
- compile/canonical/typecheck problems surfaced as review issues
Current limitation:
- when one side of the diff cannot complete full typed analysis,
sigil review
may fall back to parse-only declaration facts and call that out explicitly
sigil reviewshells out togitdirectly for selection and snapshot reads,
and it does not yet impose its own subprocess timeout; a hung prompt or repo lock will block review until git exits
Inspect Surfaces
sigil inspect validate
Use this when you suspect canonical-shape or source-form issues.
sigil inspect validate language/examples/genericFunctions.sigil
This returns:
- whether validation succeeded
- the canonical source printer output
- validation diagnostics when the source is parseable but non-canonical
Use it first for:
- declaration ordering problems
- canonical helper-surface violations
- rejected top-level shapes
- other “the compiler says this source is wrong” issues
sigil inspect types
Use this when you need solved top-level types without compiling or running.
sigil inspect types language/examples/genericFunctions.sigil
Current scope:
- top-level declaration-focused
- useful for exported/library surfaces and entry declarations
- not a nested-expression type explorer
For compiler-derived JSON codecs, inspect types also reports jsonCodecs per file. Each entry includes:
- the derived root
targetName/targetTypeId - generated helper names and solved signatures
- a normalized
wireFormatsummary for records, sums, wrappers, lists, maps,
Option, and decode-time constraint validation
This is the fastest way to confirm the exact JSON contract that derive json will accept and emit without reading generated TypeScript.
sigil inspect proof
Use this when you need the declared proof surface without waiting for a failing compile.
sigil inspect proof language/examples/functionContracts.sigil
Current scope:
- type
whereconstraints - function
requiresclauses - function
ensuresclauses matcharms and guardsifconditions
This surface inventories proof sites. It does not yet replay every solver step. Use it to answer questions like:
- where does this module introduce refinements or contracts?
- which branches participate in narrowing?
- how many proof-bearing sites exist in this file or directory?
The canonical runnable proof examples are:
language/examples/functionContracts.sigillanguage/examples/proofMeasures.sigil
sigil inspect world
Use this for topology/config/runtime-world questions.
sigil inspect world projects/topology-http --env test
Standalone files can also inspect a local top-level c world with no --env:
sigil inspect world path/to/file.sigil
This returns:
- the selected environment
- topology presence and declared dependencies
- a compact summary of singleton world entries
- the normalized runtime world template Sigil will actually use
Use it when debugging:
- missing or wrong env bindings
- HTTP/TCP dependency setup
- random/timer/log/process/fs backend differences between environments
inspect world has two scopes:
- project env inspection requires
--env - standalone single-file inspection rejects
--env - success output does not restate derivable canonical config/topology paths
- test-local
world { ... }overlays are not part of this surface
sigil inspect codegen
Use this when you need the emitted TypeScript or the derived output/span-map paths without writing artifacts.
sigil inspect codegen language/examples/genericFunctions.sigil
This returns:
- inline generated TypeScript for the requested file
- derived
.tsand.span.jsonpaths - span-map summary counts
- full module inventory for the resolved compile graph
It is useful when a runtime trace or exception already points into generated code and you need to understand the emitted shape.
Runtime Debugging With sigil run
Baseline JSON
Use:
sigil run --json
This gives one structured success or failure envelope. On runtime failures, inspect:
- top-level
phase - top-level
error.code error.locationerror.details.runtimeerror.details.exception.sigilFrameerror.details.exception.sigilExpression
sigilExpression is the precise runtime-blame surface:
- exact expression span
- exact source location
- compact error/value summary
- current-frame locals and stack when available
Trace
Use:
sigil run --json --trace
sigil run --json --trace --trace-expr
Rules:
--tracerequires--json--trace-exprrequires both--traceand--json
--trace adds bounded inline events such as:
callreturnbranch_ifbranch_matcheffect_calleffect_result
--trace-expr adds:
expr_enterexpr_returnexpr_throw
Use ordinary trace first. Add expression trace only when you need finer control flow or value flow.
Breakpoints
Use:
sigil run --json --break
sigil run --json --break-fn
sigil run --json --break-span
sigil run --json --break-fn helper --break-mode collect --break-max-hits 8
Rules:
- breakpoint selectors require
--json stopmode pauses the run early and returnsok: truecollectmode keeps running and returns bounded hit snapshots
Each hit includes:
- resolved span id and span kind
- source location
- declaration context
- current-frame locals
- stack summaries
- recent trace window
Record And Replay
Use:
sigil run --json --record .local/run.replay.json
sigil run --json --replay .local/run.replay.json
Replay is strict and artifact-owned:
--recordand--replayare mutually exclusive--replaycannot be combined with--env- replay is bound to the recorded entry path, argv, and source fingerprint
Current replay coverage:
randomtimerandtime.nowprocesshttptcpfile
Replay artifacts preserve enough information to reproduce:
- successful runs
- child exits
- exact file-operation failures
- recorded effect ordering
Use replay whenever:
- a run is flaky
- an effectful run is expensive to recreate
- you want stepping or repeated breakpoint sessions over one exact execution
Test Debugging With sigil test
sigil test is already JSON-first. Use it directly for suite-level debugging:
sigil test --trace projects/algorithms/tests/basicTesting.sigil
sigil test --break-fn helper projects/algorithms/tests/basicTesting.sigil
sigil test --record .local/tests.replay.json projects/algorithms/tests/basicTesting.sigil
Current debug-capable test flags include:
--trace--trace-expr--break--break-fn--break-span--break-mode stop|collect--break-max-hits--record--replay
Test-specific behavior:
- stop-mode breakpoints stop only the current test
- a stopped test result uses
status: "stopped" - the suite continues with later selected tests
sigil test --replaycannot be combined with--env- replay artifacts store the resolved per-test world after local
world { ... }overlays
Per-test results may include:
tracebreakpointsreplayexception
When replaying a failing test, use the exact stable results[].id later with sigil debug test start.
Replay-Backed Debug Sessions With sigil debug
Stepping is replay-backed. There is no long-lived debugger process.
Debug One Run
sigil run --json --record .local/run.replay.json app.sigil
sigil debug run start --replay .local/run.replay.json --watch user.score --watch result.value app.sigil
sigil debug run snapshot .local/debug/.json
sigil debug run step-into .local/debug/.json
sigil debug run step-over .local/debug/.json
sigil debug run step-out .local/debug/.json
sigil debug run continue .local/debug/.json
sigil debug run close .local/debug/.json
Debug One Exact Test
sigil test --record .local/tests.replay.json projects/algorithms/tests/basicTesting.sigil
sigil debug test start --replay .local/tests.replay.json --test "projects/algorithms/tests/basicTesting.sigil::cache hit returns cached value" --watch result.value projects/algorithms/tests/basicTesting.sigil
sigil debug test continue .local/debug/.json
Rules:
sigil debugis JSON-onlystartcurrently requires--replaystartmay also preload repeatable--break,--break-fn, and--break-spanselectorssigil debug test --testrequires one exactresults[].id- watch selectors are
local(.field)* snapshotreads the current stored session state without advancing executionstep-intoadvances to the next source-level pause pointstep-overstays at the current frame when possiblestep-outruns until the current frame returns or throwscontinueruns until the next breakpoint, failure, or normal exitcloseends the session and removes the session file
Every debug snapshot includes:
- current state and pause reason
- source file, span id, span kind, and location
- declaration or test context
- current-frame locals
- stack summaries
- recent trace
- replay progress
- stdout/stderr so far
- ordered
watches
Watches are session-scoped and recomputed on every pause:
ok: selector resolved successfullynot_in_scope: root local is not visible in the current framepath_missing: a later field is missing or traversal hit a non-record value
Worked Workflow
1. Compiler Or Canonical Failure
Use:
sigil inspect validate src/main.sigil
sigil inspect types src/main.sigil
Do not start from emitted TypeScript or runtime behavior if the source does not pass canonical validation and typechecking first.
2. Runtime Crash
Use:
sigil run --json --trace src/main.sigil
Read in this order:
phaseerror.codeerror.locationerror.details.exception.sigilExpressionerror.details.trace.events
If the failure looks effectful or flaky, record it next:
sigil run --json --trace --record .local/crash.replay.json src/main.sigil
sigil run --json --trace --replay .local/crash.replay.json src/main.sigil
3. Topology Or Config Problem
Use:
sigil inspect world . --env test
Check:
- declared dependency handles
- singleton backend kinds
- normalized runtime world entries
Only move to run once the selected env world looks correct.
4. Step Through A Recorded Run
Use:
sigil run --json --record .local/run.replay.json src/main.sigil
sigil debug run start --replay .local/run.replay.json --watch state.count src/main.sigil
sigil debug run step-into .local/debug/.json
sigil debug run step-over .local/debug/.json
This is the right path when:
- a trace is too dense
- you need to compare state across pauses
- you want one stable watched value across several steps
5. Debug One Failing Test
Use:
sigil test --record .local/tests.replay.json tests/
Then take the exact results[].id for the failing test and start:
sigil debug test start --replay .local/tests.replay.json --test "" --watch result.value tests/
This lets you step only that test's recorded execution without rerunning the whole suite in a live mode.
Human + LLM Workflow
The intended workflow is the same for a person and an LLM, but the machine surfaces mean the LLM can stay precise instead of guessing from prose.
If the question is about the Sigil language surface itself rather than one execution, start with sigil docs ... instead of a debug command. Use sigil docs context --list to discover curated bundles, or sigil docs search to jump to a specific syntax, stdlib, or package topic.
A good loop is:
- run one machine-readable command first
- branch on
phase - inspect the smallest relevant surface next
- replay if the behavior may vary
- step only after the run is deterministic
For an LLM, that usually means:
sigil run --json --trace ...orsigil test ...- inspect the returned code, phase, location, and exact expression
- use
inspect worldorinspect codegenonly if the failure suggests it - record/replay before suggesting fixes for effectful behavior
- use watches to pin the few values that matter across steps
For a human, the same rule applies:
- prefer one structured run over ad hoc logging first
- use replay instead of hoping the same bad run happens again
- use debug sessions when you need state transitions, not just one failure site
Related References
language/spec/cli-json.mdlanguage/spec/cli-json.schema.jsonlanguage/spec/testing.mdlanguage/spec/run-replay.schema.jsonlanguage/spec/test-replay.schema.jsonlanguage/spec/debug-session.schema.jsonlanguage/docs/TESTING.mdlanguage/docs/TESTING_JSON_SCHEMA.md