
Skill
verifiable-compiler
build formally verified compilers in Lean
Description
Use when the user wants Codex to build or make sustained progress on an end-to-end formally verified compiler in Lean.
SKILL.md
Verifiable Compiler
Use this skill for Lean-first verified compiler work. The primary artifacts are Lean definitions, compiler functions, semantics, relations, and checked theorems. Tests, fixtures, docs, and external tools are support evidence only.
The default shape is: start near the target, add one semantic abstraction layer at a time, prove the adjacent lowering completely for the source language in scope, then move up.
Start Or Resume
- Inspect local state before planning:
git status --short(if this is not a git repo, initialize with a commit);lakefile.lean,lean-toolchain, root modules, theorem files, tests, and recent build scripts;ROADMAP.mdandPROGRESS_LOG.md, if present.
- Call
get_goal. If no active goal exists, create one whose objective matches the user's concrete request in this turn. For sustained build requests, include the requested compiler scope and the end-to-end theorem standard. For narrower work, keep the objective scoped to that audit, proof-debugging, refactor, architecture review, skill edit, or pass-specific task. - When starting or resuming sustained verified-compiler work, say explicitly
that this workflow is using
$verifiable-compiler, and remind the user that future continuation requests should mention$verifiable-compiler. Do this at start/resume or handoff, not in every status update. - Use exactly two project docs by default:
ROADMAP.md: sparse checklist and current public spine.PROGRESS_LOG.md: flat timestamped log of proofs, tests, bottlenecks, architecture risks, deletions, and status.
- Every time the agent restarts from conversation compaction, append a short
timestamped
PROGRESS_LOG.mdentry before continuing, including for narrow follow-up tasks. Name it as a compaction resume, summarize the current objective/checkpoint in one sentence, and keep it concise. - Do not create feature matrices, trust ledgers, design histories, or incident reports unless the user asks. If legacy docs exist, read them as context but do not keep adding synchronized truth there.
Git Checkpoints
- Commit verified checkpoints as you go. After a meaningful theorem, boundary cleanup, refactor, or audit passes its relevant build/axiom checks, create a focused git commit before starting the next substantial proof thread.
- Keep commits honest: include only project files that belong to the verified checkpoint, and mention the Lean build or axiom audit that passed. Do not commit huge build logs, scratch outputs, or unrelated generated clutter.
- If the user says all current work belongs to the agent, treat the dirty tree as owned work for staging purposes, while still keeping commits coherent and avoiding accidental inclusion of temporary logs or secrets.
For a broad build request, the goal is not complete until concrete Lean theorem statements connect every source-language feature in scope to the declared target interpreter. Do not silently reinterpret a request for a source language as a request for a convenient accepted fragment.
For any active goal that asks to complete a layer or compiler pass, do not stop while the public theorem still takes compiler-generated evidence as an unproved input. Treat these as blockers, not residual assumptions: generated layout records, compilation certificates, replay certificates, whole-program call or callee-preservation obligations, return-layout obligations, generated label/token uniqueness tables, and proof-carrying emitted-code segments. It is acceptable to expose fundamental semantic or resource premises such as source acceptedness, input run/evaluation, initial-state relation, fuel/gas bounds, or explicit code-size/no-overflow bounds, but any compiler artifact that can be computed from the source program must either be constructed by a checked Lean theorem or remain an explicitly unfinished goal.
Theorem-Truth Checks
Before major proof grinding, check whether the theorem is true as quantified. Try to find small counterexamples involving arbitrary target PC, fuel-zero states, stack height, static-context restrictions, gas observations, external calls, revert/error outcomes, and unsupported source constructs.
If a target fact is only true for traces produced by accepted source programs, state it over those actual traces or derive it from source-facing premises. Do not keep a globally quantified target premise just because it is convenient for the proof.
When a theorem is false or overbroad, fix the statement before adding wrappers,
compatibility lemmas, or local heartbeats. Record the correction in
PROGRESS_LOG.md as theorem-boundary, deletion, or architecture-risk.
Once the project freezes its public specification — hash-pinned theorem statements, a frozen-closure checker, or a challenge/scoring regime that measures rewrites against a fixed theorem — this workflow changes mode. A frozen statement is an immutable interface: do not restate, weaken, or "improve" it, even when the boundary looks fixable; rewrites and optimizations must reprove the exact frozen statement. If a frozen statement turns out to be false or overbroad, that is a project-level incident — report it and get an explicit unfreeze decision from the user instead of editing around it. Run the project's freeze gates (hash and closure checks) alongside the usual build and axiom audits before calling any change complete.
No Unproven Compiler Paths
Do not introduce, wire in, or commit an unproven path that emits compiler output
or deployable bytecode. This includes paths named unchecked, unsafe,
artifact-only, benchmark-only, fallback, fast path, or executable shortcut.
If such a path is used by the compiler, bytecode/artifact generation, bridge
runner, benchmark harness, or user-facing validation, it must either be the
checked theorem path or have a Lean theorem proving equality/refinement to the
checked theorem path, with an axiom audit showing only standard Lean axioms.
Executable variants are allowed only when a checked equality/refinement theorem connects them to the proof-facing definition, and all callers that emit bytecode can be transported back to the public preservation theorem through checked lemmas. Existing unproven output paths are blockers to remove, prove equivalent/refining, or quarantine as non-compiler diagnostics; do not commit new work that relies on them.
Core Strategy
Build a target-up semantic tower, one complete layer at a time:
- Choose or define the declared target interpreter and observation model.
- Define the lowest useful layer above that target.
- Add exactly one real semantic abstraction in the next higher layer.
- Give that layer syntax, semantics, wellformedness/acceptance, a relation to the lower layer, compiler/lowering, and preservation theorem. You can add slices to that layer one at a time, but the layer needs to be feature-complete, not a toy version of the language, before moving on. Its feature set should cover the whole abstraction that belongs at that layer and, as far as possible before higher abstractions exist, match how that feature works in the source language being targeted. The semantics must be an independent interpreter or evaluation relation over that layer's own syntax. Defining the layer's run/eval as "lower/compile this program, then run the lower layer" is not a valid source semantics, except for an explicitly named transparent adapter boundary with no new source constructs and a recorded audit note. The semantics must also abstract the feature this layer introduces. Do not define a new source construct by exposing the lower representation it is meant to hide. For example, a locals/frame layer should have a named-environment source semantics; stack slots, layout depths, swaps, pops, or frame encodings belong in that layer's compiler/lowering relation and proof, not in the source interpreter used by higher layers.
- Compose the new theorem into the public spine.
- Run a brief theorem-boundary checkpoint before moving on.
- Only then add the next layer.
Example of good abstraction steps include labels over byte offsets, structured control over jumps, locals/frames over stack slots, expressions over primitive stack code, functions over entry labels, or objects/modules over flat functions.
Bad layers are story-shaped features (let-if-assign-return), parser fixtures,
one-off combinations (if inside for), or helper representations with no
independent semantic contract.
Before implementing a broad layer, run this checklist. This is a thinking checkpoint, not a request to create new documents:
- Grammar frozen: every construct in scope is named, and every tempting construct out of scope is explicitly excluded.
- Prior art checked: inspect the existing lower layer, adjacent completed layers, and any sibling worktree or earlier implementation the user points to before inventing a new proof route. Reuse proven theorem shapes when they fit; record why they do not if you diverge.
- Semantics frozen: outcomes, errors, effects, divergence/fuel behavior, and observable state are clear for the accepted subset.
- Theorem-truth checked: known counterexample shapes such as arbitrary PC, fuel-zero target states, stack/resource exhaustion, static context, gas observability, and external effects have been ruled out or reflected in the theorem boundary.
- Interpreter independence checked:
run/Evalis not merely lower-layer execution after compiling/elaborating the source; any exception is an explicit transparent adapter boundary, not a completed compiler layer. - Abstraction boundary checked: the layer's source semantics hides the lower representation that its new feature abstracts. Any proof terms, layouts, slots, continuations, frame encodings, return tokens, cleanup code, or other lower-level machinery needed to implement the feature live in the compiler and preservation proof, not in the source interpreter or the interface used by higher layers.
- Lower capabilities pass through: the layer does not silently shrink the already-verified lower language surface.
- Accepted restrictions classified: every nontrivial
Accepted/WFpredicate is checked against the requested scope. An explicit predicate name is not by itself a justification; if it excludes lower-layer behavior because the proof route or representation is unfinished, keep the layer incomplete unless the user explicitly narrowed the language. - Public theorem sketched: adjacent theorem and composed theorem have the right shape before helper proofs begin.
- Proof interface sketched: layout, freshness, state relation, continuation, resource, or environment assumptions are bundled when they are conceptually one invariant.
- External/resource boundary classified: gas, fuel, code size, stack depth, static-context restrictions, storage/account state, and external responses are either modeled by source semantics, rejected by acceptance, or exposed as explicit source-facing premises.
- Temporary scaffolds named: replay/certificate/helper routes have a planned replacement theorem or are kept private.
- Public certificate boundary checked: if a theorem still takes a compiler certificate, layout witness, replay witness, proof-carrying emitted-code segment, or generated-label table as an input, do not call the pass complete unless that certificate is itself generated by a checked compiler theorem or the user explicitly accepted it as a trusted input format.
- No public call oracle: for recursive source features, the public theorem must not take an all-callees-preserve, all-functions-preserve, replay, or call obligation premise. Discharge those obligations by source-fuel, evaluation, or well-founded induction, leaving only explicit resource bounds or source acceptedness assumptions at the public boundary.
- Public theorem grep: before declaring success, search the public spine for
names like
Certificate,CompilationCertificate,Replay,Obligation,ProcPreserves,Layout,Evidence,CallPreserves, and project-specific certificate names. Any remaining public occurrence must be either generated by a checked constructor theorem or justified as a fundamental resource/input premise in the audit entry.
Source-Language Completeness
When the user asks for a verified compiler for a named source language or layer,
the default obligation is the complete semantics of that source language layer.
Accepted/WF may reject malformed, ill-scoped, ill-typed, or otherwise
semantically invalid programs, but it must not reject implemented source
constructs merely because they are proof-inconvenient unless the user explicitly
approved a fragment.
Before marking such a goal complete:
- Inventory the source grammar against the source semantics and compiler.
- For every construct, record one status: supported and covered by the theorem;
semantically invalid and rejected by WF/Accepted; or explicitly out of scope
by user-approved fragment boundary. Apply the same classification to every
Accepted/WFfield, not only to syntax constructors. - If the source is a named external or imported language, name the exact reference semantics being targeted and require a checked theorem from that semantics to the compiler source semantics; elaboration through the compiler is not enough by itself.
- Confirm the source semantics is an independent interpreter or evaluation
relation over the source syntax. If
run/Evaldelegates throughcompile,lower,elaborate,toLower?, or similar, the layer is incomplete until it has its own semantics and a checked equivalence/preservation theorem, or the user explicitly accepts it as a transparent adapter boundary. - Check that supported constructs include all outcomes their source semantics admits, including errors, halts, resource failures, divergence/fuel behavior, traces, and external observations claimed by the theorem.
- Confirm theorem statements quantify over the real source semantics, not only over a compiler-success witness or replay certificate.
- If any source construct is missing semantics or theorem coverage, keep working or report a blocker; do not call the compiler fully verified.
Add this source-language audit as its own audit entry in PROGRESS_LOG.md at
major layer boundaries and before declaring an end-to-end theorem complete.
Share source and target components where this reduces real proof burden: values, arithmetic, memory/storage models, errors, traces, environments, and external state. Do not share away the compiler obligation. Source and target control semantics, layout, resource assumptions, and observations must remain explicit.
It is valid for shared primitive operations to pass through the same semantics across many layers when they are not the abstraction being introduced by that layer. For example, arithmetic, hashing, memory/storage operations, and explicitly shared external effects may reuse the same primitive meaning all the way up to the source language. That exception does not apply to a feature whose purpose is to hide a lower representation: locals should hide stack slots, structured control should hide jump encodings, functions should hide return-token/frame conventions, and so on.
Boundary Checkpoints
At major semantic pass boundaries, pause briefly before moving upward:
- Name the adjacent preservation theorem and the composed public spine theorem.
- Confirm syntax, semantics, acceptance/WF, lowering, relation, and observation/outcome model exist for the accepted feature set.
- Check for silent narrowing:
Accepted/WFpredicates must not exclude supported behavior only because the proof route is unfinished. - Check for theorem-boundary leaks: public theorem statements should not expose lower representation details, generated certificates, replay witnesses, or stale compatibility routes unless those are explicitly trusted inputs.
- Run the narrowest meaningful build, and
#print axiomsor the local equivalent for public theorem(s) when practical. - Record one concise
auditentry inPROGRESS_LOG.mdwith theorem names, command run, axiom result, and any remaining assumptions or unsupported behavior.
If the checkpoint finds a shortcut, stale public route, or accepted feature outside theorem coverage, fix it, explicitly narrow the layer with the user, or report the blocker.
External Effects And Resources
For external effects such as calls, chain state, storage, balances, precompiles, or environment responses, default to an open request/response protocol at the compiler theorem boundary. Concrete world simulation is useful only when the theorem is explicitly about that world model.
For resource-sensitive behavior such as gas, fuel, stack limits, code-size limits, static context, memory growth, or no-overflow obligations, prefer source-facing premises and actual-trace lemmas. A target-side runtime fact is acceptable only when it is derived from the accepted source run, the checked compiler output, and explicitly named resource bounds.
If a source primitive observes target resource state directly, either include that observation in the source semantics and prove it end to end, or reject the primitive in the accepted language. Do not patch it with future-answer or replay oracles in the main public theorem.
Preservation Shape
The main public theorem for an adjacent pass is a same-observation theorem. Replay certificates, traces, and local simulations are proof machinery; they should not be the public theorem boundary once the layer is stable.
Use this shape, adapted to local names:
theorem compile_sound
(hWF : Hi.WF program)
(hCompile : Hi.compile program = lower)
(hEval : Hi.Eval program initial obs)
(hRel : StateRel initial lowerInitial) :
Exists fun lowerObs =>
And (Lo.Eval lower lowerInitial lowerObs) (ObsRel obs lowerObs) := by
...
When both semantics are executable and deterministic, this may be an equality
theorem over run; when the layer erases representation details, use ObsRel.
The important claim is that the lower semantics has the same observable program
result, not merely that some hand-written replay witness exists.
Prefer strengthened, compositional theorem statements from the beginning.
Lean theorem boundaries matter. If witness data is derived from a Prop
relation such as a source run, evaluation derivation, or preservation theorem,
the public result should usually expose ∃ or Nonempty evidence. Do not make
the public route depend on returning an arbitrary structure in Type unless
that structure is produced by computation or is passed in as explicit data.
For expression-like code generation, use an arbitrary continuation/suffix:
theorem compile_correct_aux (e : Expr) (k : Code) (s : Stack) :
run (compile e ++ k) s = run k (eval e :: s) := by
induction e generalizing k s with
| ... => ...
For statement/control layers, generalize this idea. Use whichever local shape matches the layer:
- arbitrary code suffix;
- continuation or defunctionalized continuation;
- evaluation/control context;
- entry/destination labels;
- layout and no-wrap assumptions;
- state relation at an exact target PC, such as
RelAt; - outcome relation for normal return, halt, revert, throw, break/continue, or other abrupt exits.
The public proof route should be structural: sequence, context, branch, loop, call, frame/layout, and outcome lemmas compose into the same-observation theorem. If theorem names start encoding whole programs or feature combinations, stop and replace them with generic recursive/compositional theorems.
If a proof is becoming a long run of nearly identical constructors or opcodes, pause before continuing. Look for a semantic-family classifier, pass-through lemma, source syntax traversal, or relation-preservation theorem that proves the common case once and leaves only genuinely distinct cases as wrappers.
If several consecutive changes add wrappers, adapters, callbacks, bridge aliases, or compatibility routes without shrinking the public assumptions, stop and inspect the theorem boundary. Usually the right next patch is deletion or restatement, not another adapter.
After a few helper lemmas for a new construct, inspect the next theorem statement. If it needs many independent PC, label, freshness, layout, resource, or environment premises, introduce a small structure or relation that names that interface. Long premise lists are usually missing abstraction, not proof progress.
Wherever feasible, derive full preservation proofs from accepted source runs; treat assumed replay/certificate witnesses as temporary scaffolding, not the public theorem boundary.
Compiler-generated evidence is not a fundamental assumption. Before declaring a layer done, try to prove a checked constructor for every public certificate: layout, freshness, label resolution, call-site table inclusion, PC-fit, typed continuation boundary, and compiler-output equality. If construction is not yet proved, the layer is not complete; log the missing constructor and keep working or explicitly report the blocker.
Do not hide unfinished compiler evidence inside source acceptance. Accepted
may bundle source wellformedness, source semantic safety, checked resource
limits, or user-approved trusted-input contracts, but it must not contain a
compiler-generated layout, replay witness, call-site table, label-resolution
proof, procedure-body preservation proof, or semantic call oracle unless the
skill user explicitly chose proof-carrying source input as the trusted source
format. If a preservation theorem needs such evidence, either generate it with
a checked compiler theorem or keep the layer incomplete.
For recursive source features such as procedure calls, function calls, recursive loops expressed through fuel, or mutually recursive declarations, the default discharge route is induction on source fuel or on the relational evaluation derivation. Do not replace this with an assumed "all callees preserve" oracle. If a temporary all-callee/call obligation is introduced, keep it private, give it a fuel-bounded replacement plan immediately, and do not report the layer complete until the public theorem derives it.
Avoid brittle fusion lemmas. A lemma about if inside loop, switch inside
for, or assignment plus branch plus return is not a milestone unless it is a
private stepping stone being replaced immediately by generic seq/if/switchloop/outcome composition. If a fused lemma remains useful, log it as a
shortcut or architecture-risk and keep it out of the public spine.
CPS is a tool, not a mandate. Raw higher-order continuations can make Lean terms huge. Prefer a small continuation/context interface that keeps compiled programs folded and lets induction hypotheses apply under arbitrary suffixes.
Accepted Subsets
Full preservation means full semantics for the accepted subset, but an accepted subset is allowed only when the user requested a fragment or when the source language definition itself is intentionally smaller than its parser or host AST. If the user asks for a compiler for the whole source language, the accepted subset must be source-complete except for malformed or semantically invalid programs.
Do prove all relevant outcomes of accepted programs: success, errors, halts, resource failures, traces, divergence/fuel behavior, and external observations that the theorem claims to model.
Do not prove only the terminating happy path unless the theorem name and
accepted checker explicitly say that is the claim. Unsupported behavior should
be rejected by Accepted/WF, guarded by explicit theorem assumptions, or
called executable-only.
Keep these notions separate:
- parser/decoder success;
- semantic acceptance or wellformedness;
- compiler success;
- certificate checking;
- target execution success.
Accepted input must not mean "the compiler returned some." Compiler-success
theorems are useful, but they are weaker than accepted-input theorems.
Lean Blowup Rules
Treat timeouts and heartbeat explosions as architecture feedback, not merely tactic problems.
Do not let Lean normalize whole compiled programs as a proof strategy.
Context Hygiene
Preserve context for theorem state, not logs or repo tours:
- Before every read, search, or build, name the narrow question it should answer; avoid fresh "getting bearings" sweeps after a handoff identifies the next target.
- Keep build output out of context: redirect noisy
lake buildlogs to/tmpand inspect onlyerror:lines plus the smallest needed neighborhood. - Scope searches tightly. Prefer module-specific
rg,rg -n -m,rg --files,git diff --stat, and short line windows; broad repo-widerg, full diffs, generated-file searches, and widesedranges are last resorts. - On resume, trust the latest handoff/progress log unless a specific line must be checked.
- After any large tool output, retain only theorem names, file/line pointers, failing goals, and the concrete next patch.
Prefer:
- structural lemmas over syntax/control contexts;
- one-step semantic interface lemmas;
- semantic classifiers for large opcode/operator families;
- small public wrapper theorems around imported or recursive definitions;
- explicit state/layout relations instead of massive record equalities;
- named intermediate definitions to share structure;
simp only [...]with local simp sets;- controlled unfolding of one definition at a time;
- narrow module builds:
lake build Module.Nameorlake env lean path/File.lean. - recursive theorem families whose structurally decreasing syntax argument comes
early in the argument list, with
termination_byadded before the proof grows.
Avoid:
- broad
simp_allover compiler and interpreter definitions; - unfolding imported dispatchers across many constructors;
- one theorem that case-splits over a large instruction set and tries many
first | change ...branches; - exposing large recursive compiler definitions as global simp rules;
- proof terms that inline every compiled instruction or generated label;
- broad imports in theorem files;
- adding local heartbeats before checking whether the theorem/interface shape is wrong.
- continuing a proof whose statement has become a page of plumbing. Bundle the invariant and prove the compiler supplies it.
When widening primitive operations, classify by semantic family or require a small proof-carrying/accepted primitive interface. Prove PC/resource/state insensitivity once per family, then use a one-line wrapper per constructor. Do not ask Lean to rediscover the same dispatcher facts for every opcode.
Primitive Classifiers
For large primitive or opcode surfaces, introduce a small semantic-family API before broadening the source layer. This API should be a proof boundary, not a second unverified semantics.
Documentation And Logs
Code and theorem statements are the source of truth. Before reporting status, inspect the actual theorem names, statements, public imports, and build output.
ROADMAP.md should stay sparse:
# Roadmap
## Public Spine
Accepted AST -> LayerA -> LayerB -> Target interpreter
## Next Layer
- [ ] Interface: syntax, semantics, WF, relation
- [ ] Theorem-truth check and external/resource boundary
- [ ] Complete source-faithful feature set for this layer
- [ ] Lowering function
- [ ] Adjacent same-observation theorem
- [ ] Composed public theorem
- [ ] Boundary checkpoint
PROGRESS_LOG.md is append-only and flat:
- 2026-05-12 18:17:26 - proof - proved `CompilerPreservation.block` and `compile_whole_program_sound_of_run`; command `lake build`; result green.
- 2026-05-12 18:20:00 - architecture-risk - primitive widening proof unfolds full opcode dispatcher; replace with semantic family interface before adding more ops.
Use tags such as proof, test, audit, bottleneck,
theorem-boundary, architecture-risk, shortcut, consolidation,
deletion, and incident. For risks and shortcuts, name the artifact, why it
is risky, and the generic theorem/interface that should replace it.
Keep logs current, not just append-heavy. When a bottleneck, shortcut, or risk is resolved or superseded, add a short entry saying so and name the replacing theorem or interface. Do not leave stale warnings that still read as active.
Git Preservation
Use git to preserve meaningful theorem-bearing states. Commit only coherent milestones, preferably green:
- initial Lean package setup;
- first target-reaching theorem;
- new public layer or theorem boundary;
- accepted-language/theorem-boundary change;
- byte encoder or real-machine bridge;
- deletion of obsolete fixture/story routes;
- audited stable proof milestone.
Never revert user changes. Before pausing or finishing substantial work, run
git status --short and make the handoff state clear.
Anti-Patterns
Stop and repair the architecture if any of these appear:
- full high-level language first, proofs later;
- theorem ends at an IR while the claim says end-to-end;
- special theorems for
ifinsideloopor other combinations; - fused feature lemmas become public proof routes instead of temporary private scaffolding;
- parser helper recognizes a whole story instead of independent constructs;
- semantic classifier or helper interpreter used as if it were the real target without an equality/refinement theorem;
- source semantics changed to match a compiler bug;
- broad public imports retain obsolete compiler routes;
- concrete external-world simulation is used where an open protocol is the real theorem boundary;
- observed target resources such as gas are patched by replay/future-answer oracles instead of source semantics, acceptance rejection, or explicit source-facing premises;
- wrapper, adapter, callback, or compatibility-route churn continues without reducing public assumptions;
- proof relies on
sorry,admit, newaxiom,unsafe,partial, or proof-critical external assumptions without excluding them from the claim; - tests or external review are treated as verification;
- Lean timeout is answered by adding more feature code.
Completion Gate
Do not call the compiler verified because tests pass, the compiler runs, or a scaffold theorem exists.
Before reporting a verified milestone:
- Inspect the accepted compiler path.
- Name the exact top theorem(s).
- Run the narrowest meaningful build command, and full build when practical.
- Run
#print axiomsor the local equivalent on public top theorem(s) when practical. - Scan for
sorry,admit,axiom,unsafe, andpartial. - Check public imports for obsolete fixture/story/legacy routes.
- Check public theorem arguments for compiler-generated certificates or layout evidence. They must be discharged by checked constructors, be private helper inputs, or be explicitly named as trusted external inputs approved by the user. Otherwise keep working.
- Inspect
Accepted/WFrecords used by the public theorem. They must not smuggle in compiler-output equality, label layout, replay certificates, call/return layout, generated-token uniqueness, or semantic preservation oracles. They also must not exclude lower-layer capabilities merely because the representation or proof is unfinished. If they do, split those fields out and discharge them, explicitly narrow the requested source language with the user, or keep the milestone incomplete. - Check that resource and external-effect premises are source-facing or actual-trace-derived, not globally quantified target facts.
- State remaining assumptions and unsupported features plainly.
The goal is complete only when every source-language feature in the requested scope has a checked path from trusted input or AST through acceptance, semantics, compiler/lowering, target interpreter, and theorem. If a fragment was explicitly requested, say so in the theorem names or surrounding API and audit the fragment boundary. Otherwise report the current work as a theorem-bearing milestone and keep working on the missing source semantics.
References
Load only when useful:
references/verified-compiler-patterns.md: theorem-boundary patterns and classic verified-compiler references.references/process-lessons.md: earlier process failures to avoid.