Skip to content

RFC / KEP-1: Taclet-generating transformers (lemmas instead of metaconstructs, built-in rules, etc.)#3893

Draft
unp1 wants to merge 20 commits into
mainfrom
bubel/lemma-taclet-spike
Draft

RFC / KEP-1: Taclet-generating transformers (lemmas instead of metaconstructs, built-in rules, etc.)#3893
unp1 wants to merge 20 commits into
mainfrom
bubel/lemma-taclet-spike

Conversation

@unp1

@unp1 unp1 commented Jul 5, 2026

Copy link
Copy Markdown
Member

Status: RFC / proof-of-concept.

Companion documentation (concept, GUI walkthrough,
beyond-OSS): https://keyproject.github.io/key-docs/keps/kep-0001-taclet-generating-transformers/.

Reference to Discussion 2019 (original idea @wadoon): Taclet generation Language (at the moment internal only)

Addresses similar issue as #3707 by @Drodt but from a different angle
KEP moniker coined by @WolframPfeifer ;-)

Motivation

Several parts of KeY perform many calculus steps at once in Java: term
transformers (metaconstructs like #add) and built-in rules like the One Step
Simplifier (OSS).

Their advantage is performance and the ability to perform complex transformations.
But they are implemented in Java and therefore harder to inspect as a taclet.

This PR explores a middle path: instead of a transformer that mutates the
proof, a generator produces a specialized, metaconstruct-free taclet
that captures the transformation. It is introduced (a recorded step),
applied (an ordinary recorded taclet application), and provable (a
soundness obligation is created and discharged in the base calculus). The
performed transformation becomes an inspectable, separately certifiable
artifact — and a changed result surfaces immediately, because the recorded
name hashes the outcome.

The state of the implementation is suitable for trying it out and discussing
whether to carry it forward or not. It is not finalized. Also the show case in
this example is the OSS, the framework is more general and not restricted to OSS.

In principle one could write complex transformations that establish Groebner bases in
one step and proof that transformation later correct as part of the soundness proof
obligation for the taclet.

What this changes, at a glance

  • de.uka.ilkd.key.rule.lemma — the framework: LemmaTacletGenerator
    (contract: context-free via \assumes, in the provable fragment,
    deterministic content-derived names), LemmaIntroductionRule (built-in),
    GeneratedLemmaRegistry, GeneratedLemma (lazy soundness obligation),
    LemmaProver (batch discharge), TransparentProofSaver,
    LemmaElaboratingProofReplayer (a-posteriori elaboration).
  • OssLemmaGenerator + OneStepSimplifier.computeSimplification(...) — OSS as
    the first real generator (a pure extract-method exposing its core; no change
    to opaque OSS behavior).
  • Strategy option One Step Simplification: Enabled / Disabled / Transparent.
  • ProofCorrectnessMgt tracks unproven generated lemmas
    (CLOSED_BUT_LEMMAS_LEFT).
  • GUI: a Lemmas tab in the proof-management dialog (per proof, grouped by
    generator + content, batch-provable), Save Transparent Proof…, and an
    "abandon all proofs of an environment" cleanup.
  • CLI: --save-transparent.

Two proof steps

p & true & (q | true) -> true & p, OSS Enabled:

0: One Step Simplification      (one opaque node, hides 5 rewrites)
  1: closeTrue

Same problem, OSS Transparent:

0: introduce_ossLemma
  1: ossLemma_fbf1a98ed8f4__0    // \find(p & true & (q|true) -> true & p) \replacewith(true)
    2: closeTrue

How generators map to lemmas

OssLemmaGenerator turns an aggregated F ↝ F' into a ground taclet; the
context formulas of OSS's replace-known steps become an \assumes clause with
\inSequentState (mirroring OSS's own discipline). Modality-containing
formulas are outside the provable fragment and stay with the ordinary calculus.
Obligations are created lazily (off the search path) and proved with OSS off from the individual base rules the lemma aggregates, never by the aggregation under scrutiny.

Two operating modes

  • In search — the Transparent strategy option. In transparent mode OSS is
    applicable nowhere and does not steal its rule sets from the goals, so
    modality-free formulas go through generated lemmas while everything else is
    simplified by the ordinary calculus in individual steps. Proofs contain no
    opaque OSS node at all
    . A small focus-conditional cost penalty makes the
    lemma path win the scheduling race on eligible formulas without disturbing
    symbolic-execution scheduling.
  • A posteriori — find the proof with fast opaque OSS, then elaborate the
    finished proof into a transparent one (this is what "Save Transparent Proof"
    does). Search stays fast; transparency is produced on demand.

Design points for discussion

  1. Explicit two-step recording is permanent; implicitness is display-only.
    No proof-format migration; folding intro+apply into one node would be a
    view filter (not in this PR).
  2. Generated taclets are not yet serialized into the proof file. Transparent
    proofs load and replay-to-closed, but replay re-runs the generator; the
    content-hash names make this deterministic, and a changed result fails loudly
    ("taclet not found"). Serializing the taclets — so a checker needs only the
    taclet text + its obligation and the generator drops out of the trusted base
    — is the natural next step, deliberately out of scope.
  3. Fragment boundary. Modality-free (incl. update-laden) formulas are
    lemma-eligible; program modalities are not. Generalizing update-prefix
    simplification (schematizing the unchanged modality kernel) would widen the
    fragment and is a designed follow-up, not built here.
  4. Bound-uniqueness check scoped to schema variables. Taclets generated from
    proof formulas legitimately re-bind concrete variables; the check's own
    documentation says the restriction is about schema variables, so this aligns
    code with intent rather than weakening an invariant.

Trying it out

  • Toy generator: ./gradlew :key.ui:run, load lemmagen-demo.key, right-click a
    5 + 7 subterm → introduce_addLitLemma, then apply the generated taclet.
  • OSS in the GUI: set One Step Simplification to Transparent, run a FOL/arith
    proof, inspect the ossLemma_… steps; open Proof Management → By Proof →
    Lemmas to prove the obligations.
  • CLI: ./gradlew :key.ui:run --args="--auto --save-transparent <file>.key".
  • Reuse measurement: ./gradlew :key.core:runOssReuseProbe.

Testing

New tests under de.uka.ilkd.key.rule.lemma cover: introduction / application /
obligation / replay / reuse (toy); OSS lemmas with and without assumptions,
including update-formula obligations; transparent-mode automode on
computation / SumAndMax / BinarySearch (and that stock mode is unchanged);
non-circular obligations (OSS off); quantifier-obligation generation (regression
for the DefaultLemmaGenerator fix); a-posteriori elaboration; batch proving,
content grouping, status transition and no-flooding. ProveRulesTest confirms
the shared translation change is inert for the standard taclet base.

Not for merge as-is / follow-ups

  • lemmagen-demo.key sits at the repo root for reviewer convenience; move under
    key.ui/examples/ (or drop) before merge.
  • \rules serialization of generated taclets (generator-independent checking).
  • Cross-proof / environment-level lemma library (reuse across proofs).
  • Update-prefix kernel generalization (widen the fragment past modality-free).
  • Proof-tree view filter to collapse intro+apply visually.
  • The No proof loaded selection-listener crash exposed by bulk-abandon is
    fixed upstream in Fix GUI crashes when no proof is loaded, and remove dead MainWindowTabbedPane #3892 (not duplicated here).

Code with AI tooling support.

@unp1 unp1 self-assigned this Jul 5, 2026
@unp1 unp1 added the RFC "Request for comments" is the appeal for making and expressing your opinion on a topic. label Jul 5, 2026
unp1 added 18 commits July 5, 2026 22:12
Instead of performing many proof steps programmatically (term transformers,
built-in rules), a LemmaTacletGenerator packages the transformation as a
specialized, metaconstruct-free taclet. A LemmaIntroductionRule (built-in)
introduces the generated taclet goal-locally together with a soundness proof
obligation created via the existing taclet lemma machinery; rewriting with the
lemma is then an ordinary, separately recorded taclet application.

Design properties validated by the end-to-end test:
- two-step recording (builtin introduction + taclet app) saves and replays;
  replaying the introduction re-runs the deterministic generator, and the
  recorded taclet application resolves against the regenerated taclet by its
  content-derived name
- the taclet carries a GeneratedLemmaJustification pointing to its soundness
  proof (not an axiom, not justified by the introducing rule)
- per-proof GeneratedLemmaRegistry guarantees one taclet instance and one
  proof obligation per lemma name (reuse across branches and after pruning)
- the soundness proof obligation closes automatically

Toy generator: AddLiteralsLemmaGenerator folds sums of integer literals,
mirroring the add_literals/#add system taclet whose right-hand side is
computed by Java code at application time. The system taclet stays untouched.

Created with AI tooling support
…racking

Two refinements to the taclet-generating transformer spike:

1. The soundness proof obligation of a generated lemma is no longer created
   during the introducing rule application but lazily on first request
   (GeneratedLemma.getOrCreateSoundnessProof). Rule application - and thus
   proof search - now only pays for building the taclet and inserting it into
   the goal-local index; PO creation and discharge become a deferrable batch
   concern. (A name-recorder probe showed the *AtPre newnames on the
   introduction node predate the rule application - they are load-time
   proposals flushed by the first applied rule, pre-existing KeY behavior
   unrelated to PO creation.)

2. ProofCorrectnessMgt now tracks generated lemmas: a closed proof that used
   a lemma taclet whose soundness proof is missing, disposed, or open is
   downgraded to CLOSED_BUT_LEMMAS_LEFT (transitively through the soundness
   proofs, which live in copied init configs and are inspected directly).
   The pass runs before the contract fixpoint so dependent contract proofs
   observe the downgrade.

Fixed along the way: ProofPruner removes the justification of goal-locally
introduced taclets on pruning; the registry now re-registers the identical
justification instance when a cached lemma is re-introduced. The end-to-end
test covers laziness, replay-before-PO, the pruning path, and both proof
status transitions.

Created with AI tooling support
Extract a public entry point computeSimplification(Goal, PosInOccurrence,
Protocol) returning the aggregated simplification result (simplified formula,
number of applied rules, used context formulas) without modifying goal or
proof. This is the OSS core computation exactly as apply() performs it,
usable by clients that want to capture the aggregated transformation in a
different form (e.g., as a generated lemma taclet). No change to the rule's
own behavior; requires the simplifier to be active for the proof.

Created with AI tooling support
…taclets

OssLemmaGenerator packages an aggregated one-step-simplification F ~> F' as
a ground rewrite taclet: the context formulas used by replace-known steps
become an \assumes clause combined with \inSequentState (mirroring the
discipline OSS itself follows: replace-known stops at update and modality
boundaries); without replace-known the taclet is an unrestricted equivalence
rewrite. Introduced interactively via OssLemmaIntroductionRule (registered in
JavaProfile; automation-inert), reusing the LemmaIntroductionRule two-step
recording.

Taclet names are content-derived: SHA-256 over a canonical serialization of
find, assumptions (with polarity), and replacewith - term hash codes are not
guaranteed stable across JVM runs, and replay resolves taclets by name.
Including the result means a changed simplifier outcome surfaces on reload
as a missing taclet rather than a silently different rewrite.

Generator applicability excludes formulas containing modal operators (outside
the lemma PO fragment); update-laden formulas are supported - the end-to-end
test discharges the proof obligation ({i:=1}(i=1)) <-> true, confirming
UpdateApplication passes the taclet lemma translation. Second test covers the
assumes-carrying case including assumption-instantiated application and
save/reload replay of both steps.

Created with AI tooling support
Runs bounded automode with the stock one step simplifier on a selection of
example problems, keys every OSS application by the registry's content key
(printed find formula + used context formulas with polarity), and reports
total vs. distinct applications restricted to the lemma-eligible
(modality-free) subset, plus the aggregated micro steps a result cache would
have skipped.

Findings on the current selection: 91% of OSS applications are
lemma-eligible; 35% of eligible applications are content-duplicates
(check_jdiv 46%, ReverseArray 24%, arrayMax 18%); 13.5% of aggregated micro
steps are savable (a lower bound - cache hits also skip the fixpoint's
unsuccessful scanning, invisible post-hoc). The lemma path is performance
neutral-to-positive while adding inspectability and provability.

Created with AI tooling support
LemmaElaboratingProofReplayer copies a proof onto a fresh proof of the same
problem, replacing every lemma-eligible one step simplifier application by
the two-step lemma form (introduce_ossLemma + generated taclet application);
non-eligible applications (modal operators, unlocatable positions) are copied
unchanged, so elaboration never fails a proof that copies cleanly. This
decouples proof search from transparency: search runs with the fast opaque
rule, the finished proof is elaborated into a form where every aggregated
simplification is a declarative, separately provable artifact.

Position relocation uses AbstractProofReplayer.findInNewSequent (widened to
protected), which compares modulo proof irrelevancy - strict term equality
fails as soon as term labels diverge between original and elaborated proof.

End-to-end test on standard_key/arith/computation.key: 36 OSS applications,
16 elaborated (exactly the modality-free subset), 20 copied, 16 generated
lemmas all certified by discharging their soundness proof obligations, the
elaborated proof closes, and each elaborated step adds exactly one node
(597 -> 613).

Created with AI tooling support
Three user-facing integrations of the taclet-generating transformer machinery
for the One Step Simplifier.

Strategy option: One Step Simplification gains a third value 'Transparent'
(OSS_TRANSPARENT) besides Enabled/Disabled. In transparent mode the simplifier
machinery stays active but yields lemma-eligible (modality-free) formulas to
OssLemmaIntroductionRule, which the strategy applies in its place (costed
identically via the OSS feature; the introduced concrete-ruleset taclet is
then applied normally). Formulas with modal operators are still simplified by
the opaque rule. OneStepSimplifier.isApplicable now partitions on transparent
mode; canSimplify exposes the unpartitioned notion for the generator. The
lemma introduction rule gained a guard so an already-available lemma is not
re-introduced (avoids non-termination under automation).

CLI: --save-transparent additionally writes <file>.transparent.proof in batch
mode via the new TransparentProofSaver (elaborates onto a fresh proof of the
same problem and saves; original untouched).

GUI: SaveTransparentProofAction in the File menu, the proof/task-list context
menu, and as a button in the proof-closed statistics dialog (background
worker, status-line feedback).

Created with AI tooling support
Extends the proof management dialog (the proof obligation browser) with a
'Missing Lemmas' tab listing, for the selected proof, the generated lemmas it
still depends on whose soundness proof obligation has not been discharged
(not created, or created but open). Entries can be selected individually or
all at once and loaded as side proofs into the same proof environment, where
they appear in the task tree and can be proved like any other obligation.

Core support:
- GeneratedLemma retains the soundness ProofAggregate and exposes
  getOrCreateSoundnessProofAggregate() (what a UI registers), plus
  isSoundnessProofPresent()/isProven() status.
- GeneratedLemmaRegistry.getMissingLemmas() enumerates the not-yet-proven
  lemmas; getIfPresent(proof) inspects without attaching a registry.

GUI:
- MissingLemmasPanel (multi-select list, 'Select all' / 'Load selected as
  side proofs'); creating an aggregate registers it in the environment (done
  by GeneratedLemma) and the panel adds it to the UI via
  registerProofAggregate.
- ProofManagementDialog gains the tab, refreshed from the selected proof.

Test TestMissingLemmas drives the lifecycle headlessly: all lemmas initially
missing, creating a side proof registers it in the environment and leaves the
(open) lemma missing, discharging it removes it from the missing set, and
re-requesting returns the same proof (no duplicate).

Created with AI tooling support
Transparent automode on SumAndMax ran away: thousands of introduce_ossLemma
steps, taclets that never applied, visually no-op lemmas, proof not closing
(12000-node cap: 11200 introductions, 59 lemma applications, open).

Root cause 1 - lemma name aliasing across branches: formulas on sibling
branches can be equal in their printed form yet contain distinct proof-local
symbol instances (program variables, skolem constants) sharing the same name.
The purely content-derived (print-based) lemma names aliased such formulas,
so the registry handed branch B a taclet built from branch A's instances; it
could never match, and every automation round re-introduced it. Fix: OSS
lemma names are qualified with the introduction node's tree-structural id
(Node.getUniqueTacletId, the mechanism QueryExpand uses), which is
replay-stable, branch-distinguishing, and prune-consistent. Within-branch
reuse is unaffected (the introduced taclet is inherited and applied). The
registry additionally verifies on every name hit that the cached taclet's
find equals the regenerated one, turning any residual aliasing into a
RuleAbortException instead of a dead taclet.

Root cause 2 - semantic no-op lemmas: aggregated simplifications whose result
equals the formula up to renaming and term labels produced worthless lemmas
and kept the formula eligible forever. The generator now rejects them and
vetoes the formula; the veto is proof-local state in the registry (a JVM-wide
cache would leak vetoes into the replay of a reloaded proof).

Root cause 3 - index-dependent applicability: the previous guard against
re-introduction consulted the goal-local taclet index in isApplicable. Rule
applications are queued, so the original run could legitimately record an
introduction that the guard would reject when re-evaluated freshly - which is
exactly what proof replay does, making a few recorded introductions
unreplayable. Built-in applicability is now pure in the formula; occasional
redundant introductions are harmless (unique names) and replay faithfully.

SumAndMax with transparent automode now closes (2568 nodes, 241
introductions = 241 distinct lemmas, 229 applied, opaque OSS only on modality
formulas, no no-op lemmas) and the saved proof replays to closed; kept as
regression test TestTransparentModeSumAndMax.

Created with AI tooling support
Circularity (soundness bug): a generated lemma's soundness proof obligation
inherited the main proof's strategy settings. In transparent mode that meant
the obligation was discharged by generating and applying further lemmas -
re-deriving the very simplification it must justify from first principles, a
circular and non-terminating justification (probe: 2 introductions + 1 lemma
application inside the obligation proof). GeneratedLemma now forces the opaque
one step simplifier on the proof-obligation configuration, so soundness is
established in the base calculus. Regression test TestLemmaSoundnessNonCircular
asserts the obligation runs OSS_ON and contains no introduce_ossLemma /
ossLemma_ steps.

Duplicate task-tree entries: creating a lemma soundness proof registers it in
the proof environment, and the user interface already listens on the
environment (proofRegistered -> registerProofAggregate -> task tree). The
Missing Lemmas panel registered it a second time explicitly; that call is
removed. The panel now also runs an onLoaded action so the proof management
dialog closes after loading (previously it stayed open).

Created with AI tooling support
Replaces the flat, dialog-global 'Missing Lemmas' tab with a per-proof
dependency view living beside the used contracts, and organizes the many
generated lemmas for overview.

Placement: the 'By Proof' tab's right pane is now a Contracts | Lemmas
tabbed pane, both scoped to the selected proof (the top-level Missing Lemmas
tab is gone). This mirrors the used-contracts dependency view for lemmas.

Overview: LemmaDependencyPanel shows the selected proof's lemmas as a tree
grouped by generator and, within each generator, collapsed by content
(GeneratedLemmaRegistry.groupByContent) - the many introduction-point-distinct
instances of the same simplification appear as one row with a count and a
proven/open status. On SumAndMax this turns ~241 flat rows into a handful of
distinct simplification shapes.

Actions:
- 'Load selected as side proofs' creates the obligation proofs for the
  selected generator/content rows (registered in the environment, shown in
  the task tree).
- 'Prove all lemmas...' batch-discharges via the new core LemmaProver: each
  obligation is proved with a user-chosen step bound (default 10000);
  obligations that do not close stay open for manual work; the obligation
  proofs can optionally be saved to a directory. Runs in the background;
  robust to a single obligation whose automatic search fails.

Core additions (headless-tested in TestLemmaProverAndGrouping):
GeneratedLemma.generatorName()/contentKey();
GeneratedLemmaRegistry.getLemmasByGenerator()/groupByContent(); LemmaProver.

Created with AI tooling support
…igations

Two soundness-relevant fixes to the generated-lemma proof obligations.

(1) DefaultLemmaGenerator dropped concrete bound variables. Its rebuild()
kept a quantifier's bound variable only when it was a schema variable
(VariableSV); hand-written taclets only ever bind schema variables, so this
was never exercised. Lemmas generated from concrete proof formulas, however,
contain concrete quantifiers (\forall i; ... over a LogicVariable), and those
bound variables were dropped, so a quantified subterm was rebuilt with an
empty bound-variable list and failed the term arity check - a
TermCreationException during obligation generation (not a rule soundness
problem and not automation, as first suspected). rebuild() now passes concrete
bound variables through unchanged. The change is inert for schema-variable
taclets; ProveRulesTest (203 standard taclet proofs) still passes, and
SumAndMax's quantifier lemmas now both generate and close (regression test
testQuantifierLemmaObligationsGenerateAndClose).

(2) The obligation is now proved with the one step simplifier switched off
entirely, not merely downgraded from transparent to opaque. Opaque OSS still
performs the same aggregated simplification in one hidden step, which would
close a lemma's obligation by exactly the transformation under scrutiny. With
OSS off, soundness is established from the individual base-calculus rewrite
rules the lemma aggregates.

Created with AI tooling support
Four defects found by GUI testing on BinarySearch (contract PO, transparent
mode), each with a distinct root cause:

(1) BoundUniquenessChecker rejected generated lemmas whose find and assumes
clauses share bound variables ('A bound SchemaVariable variables occurs both
in assumes and find clauses'). The restriction exists - per the checker's own
documentation - for schematic bound variables, which would have to match the
identical quantified variable everywhere so that the taclet would almost
never apply. Taclets generated from proof formulas legitimately share
concrete LogicVariable binders between assumes and find (both stem from the
same sequent). The check now applies to schema variables only. This is the
third schematic-only assumption uncovered by generated concrete taclets
(after DefaultLemmaGenerator's binder handling and the naming/aliasing
issue).

(2) The same exception, thrown out of lemma generation during automated
search, aborted the strategy mid-run ('stops after 95 steps').
OssLemmaGenerator.generate now converts any taclet-construction failure into
a veto of the formula plus a clean RuleAbortException; automated search can
no longer be halted by a generation failure.

(3) InitConfig.deepCopy copied proof-local justifications. Entries that
reference nodes of the source proof (addrule taclets such as
replaceKnownSelect, generated lemmas) collided with rule re-introductions
when a proof is elaborated or replayed onto a copied configuration ('A rule
named ... has already been registered') - a pre-existing hazard for stock
addrule taclets as well. RuleJustification gains isProofLocal() (true for
RuleJustificationByAddRules and GeneratedLemmaJustification), and
RuleJustificationInfo.copy() skips proof-local entries. The lemma registry
additionally replaces foreign same-name entries defensively.

(4) TransparentProofSaver produced unloadable files for proofs of generated
proof obligations: PO-created program variables are not part of the problem
header, and without the \proofObligation section the saved file cannot
re-create them. The elaborated proof is now registered with the original's
ProofOblInput, so saving emits the obligation section and the file loads.

Regression test TestTransparentModeBinarySearch covers the full workflow:
transparent automode closes, the transparent form saves and replays to
closed including the lemma introductions, and every lemma soundness
obligation closes in the base calculus. Full key.core suite passes.

Created with AI tooling support
Replaces the hybrid transparent mode (opaque OSS on modality formulas) by a
fully transparent one, following the observation that the opaque residue was
almost exclusively update-prefix simplification in front of modalities
(probe on SumAndMax: 38 of 39 opaque applications on update-application
formulas; ~460 of ~480 aggregated micro steps from the update rule sets).

In transparent mode the one step simplifier now
- is applicable nowhere (previously it kept handling modality formulas), and
- no longer takes the captured rule set taclets out of the goals' rule
  indices (appsTakenOver stays empty); its internal indices still serve as
  the computation core of the lemma generator.

Formulas outside the lemma fragment are thus simplified by the ordinary
strategy in individual, fully visible steps - transparent proofs contain no
opaque aggregation of any kind. (For contrast: an opaque OSS step persists
nothing about the rules it aggregated - only position and used context
formulas are saved, and replay re-runs the whole fixpoint trusting that the
current rule base reproduces the result.)

Fixed in passing: refresh() short-circuited on mode flips for an unchanged
proof - the loader's initial refresh (opaque, default settings) had already
captured the taclets, and switching to transparent neither restored them nor
rebuilt, leaving goals without simplification rules entirely (automode
saturated after a few steps). refresh() now shuts down (restoring any
taken-over taclets) before re-initializing on any state change.

computation.key: 775 nodes with OSS off, 728 fully transparent (15
introductions, 10 lemma applications; a few lemmas are obsoleted by
individual steps between introduction and application - scheduling to be
tuned in a follow-up), 597 opaque. Old transparent proofs containing opaque
modality-OSS steps no longer replay (pre-release format change).

Created with AI tooling support
Tunes the fully transparent mode so that aggregated lemmas, not individual
rule applications, perform the simplification wherever the lemma path
applies. Baseline measurements (computation/SumAndMax/BinarySearch) showed
23-35% of introductions wasted and 130-145 captured-rule-set singles firing
on modality-free formulas.

Scheduling:
- Generated lemma taclets carry the new rule set 'generatedLemma' (declared
  in ruleSetsDeclarations.key), bound like 'concrete' (-11000 plus depth
  scaling), so a blanket treatment of the captured rule sets cannot penalize
  the lemma applications themselves.
- In transparent mode, applications of the seven OSS-captured rule sets are
  penalized on formulas without executable code (focus-conditional feature) -
  exactly where the lemma path competes; symbolic-execution scheduling is
  untouched, and the rules remain applicable everywhere (completeness).
- The penalty is deliberately small (+2000): it only breaks the cost tie
  with the lemma path. A large penalty inverts cost orderings that encode
  termination invariants of the strategy - observed as an unbounded
  bounded-sum descent, where the unrolling rule (enlarging, about -2000)
  outranked its penalized empty-range counterpart (concrete) forever.

Determinism (found by elaborating a transparent proof onto a fresh copy):
- The simplifier's captured-taclet indices were built in set-iteration
  order; when several rules match at one position, proof instances of the
  same problem could simplify differently. The taclets are now sorted by
  name before index construction.
- The sub-origins of merged origin term labels are collected in object
  identity order, so the printed form of otherwise identical terms differs
  between proof instances. Lemma names and content-grouping keys are now
  computed from label-stripped terms (LemmaTacletGenerator.removeTermLabels);
  labels are proof metadata, and lemma identity is secured by the
  introduction-node id.

Also: GeneratedLemma.aggregatedSteps() records how many base calculus steps
a lemma aggregates (measurement and display metadata; the generator returns
a GeneratedTaclet record).

Results: wasted introductions 0 on all three examples (from 5/55/66),
captured singles on modality-free formulas roughly halved (rest are
legitimate last-resort applications), total aggregated coverage increased,
node counts and closure unchanged. Full key.core suite passes.

Created with AI tooling support
…atus

Two GUI defects reported on BinarySearch: after 'Prove all lemmas' the
depending proof still showed 'closed but lemmas left' even though all lemmas
were proven, and the batch registered all obligations, flooding the task tree
with closed side proofs.

The proof status was in fact correct at the model level (proving a lemma
obligation triggers the depending proof's status recomputation, which reads
the obligation proof object, not its environment registration). The dialog
simply never refreshed: updateGlobalStatus runs once when the dialog opens.

Changes:
- GeneratedLemma separates obligation creation from environment registration.
  getOrCreateSoundnessProofAggregate now only creates; registerInEnvironment
  (idempotent) registers so a user interface surfaces the obligation.
- LemmaProver registers only obligations that remain open (for manual work);
  closed obligations are certificates and stay unregistered, so a batch does
  not flood the environment. The depending proof's status still tracks them.
- LemmaDependencyPanel: 'Load selected as side proofs' registers explicitly;
  after 'Prove all lemmas' it recomputes the depending proof's status
  (robust against proof-tree listener ordering) and refreshes the dialog's
  status display via a callback.

Regression testBatchProvingUpdatesStatusWithoutFloodingEnvironment: proving
all lemmas turns the status to CLOSED automatically and leaves the
environment's proof count unchanged.

Created with AI tooling support
Right-clicking an environment node in the Loaded Proofs tree now offers
'Abandon All Proofs of Environment', which disposes every proof of that
environment after a single confirmation. Previously an environment that had
accumulated many proofs (e.g. lemma side proofs loaded in bulk from the
proof management dialog) could only be cleared by abandoning each proof
individually or restarting KeY.

The proofs are collected before disposing, since disposing a proof removes
its node from the tree.

Created with AI tooling support
Batch proving many lemma obligations gave no feedback while it ran. It now
shows a small modeless progress dialog with a progress bar (done / total) and
a Cancel button. LemmaProver.proveAll gained a progress listener that reports
after each obligation and can request cancellation (the remaining,
unprocessed lemmas are then simply not attempted); the existing three-argument
overload delegates to it with no listener. The panel drives the bar via the
SwingWorker's publish/process and disposes the dialog when done.

Created with AI tooling support
@unp1 unp1 force-pushed the bubel/lemma-taclet-spike branch from bf9bf19 to 118a542 Compare July 5, 2026 20:12
@unp1 unp1 changed the title RFC / KEP-1: Taclet-generating transformers (lemmas instead of opaque steps) RFC / KEP-1: Taclet-generating transformers (lemmas instead of metaconstructs, built-in rules, etc.) Jul 5, 2026
@wadoon

wadoon commented Jul 5, 2026

Copy link
Copy Markdown
Member

There was also a wiki entry on this from the discussion in 2019: https://git.key-project.org/key/key/-/wikis/Taclet-Generation-Language

unp1 added a commit to KeYProject/key-docs that referenced this pull request Jul 5, 2026
@unp1

unp1 commented Jul 5, 2026

Copy link
Copy Markdown
Member Author

There was also a wiki entry on this from the discussion in 2019: https://git.key-project.org/key/key/-/wikis/Taclet-Generation-Language

Thanks! I put the link at the top of PR description.

@unp1 unp1 force-pushed the bubel/lemma-taclet-spike branch from 6329c14 to ee3e2b7 Compare July 5, 2026 20:55
unp1 added 2 commits July 5, 2026 23:50
Removes two batch-mode proof outputs (lemmagen-demo.auto*.proof) and a
statistics CSV that were swept in by a broad 'git add' while wiring up the OSS
lemma generator. Only the intentional demo problem lemmagen-demo.key remains.

Created with AI tooling support
@unp1 unp1 force-pushed the bubel/lemma-taclet-spike branch from ee3e2b7 to 4294be3 Compare July 5, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RFC "Request for comments" is the appeal for making and expressing your opinion on a topic.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants