Prerequisites. Direct starred prerequisites: none. No later core chapter depends on this route.
Suppose a search procedure is asked to prove 𝐴→(𝐵→𝐴). It may introduce two hypotheses and return the first one. The result is safe only if the object returned is the term 𝜆𝑥.𝜆𝑦.𝑥:𝐴→(𝐵→𝐴) and the ordinary kernel checks that judgment. A Boolean answer from the search procedure would place the search procedure inside the trusted base. The construction below instead makes every successful search carry a kernel-checkable proof term.
Goals and validations
Fix the dependent core and kernel checker of chapter 48. No new kernel rule is added in this chapter.
A goal is a pair 𝑔=(Γ∣𝐺), where Γ𝖼𝗍𝗑 and Γ⊢𝐺𝗍𝗒𝗉𝖾. A validation from goals 𝑔𝑖=(Γ𝑖∣𝐺𝑖), for 1≤𝑖≤𝑛, to 𝑔=(Γ∣𝐺) is a partial meta-level function 𝑉 whose domain contains every tuple of kernel proofs of the displayed goals and such that (Γ𝑖⊢𝑝𝑖:𝐺𝑖forevery𝑖)⟹Γ⊢𝑉(𝑝1,…,𝑝𝑛):𝐺. Write each context as a telescope Γ𝑖=(𝑥𝑖1:𝐴𝑖1,…,𝑥𝑖𝑘𝑖:𝐴𝑖𝑘𝑖). A contextual metavariable for 𝑔𝑖 is a fresh declaration ?𝑚𝑖:(𝑥𝑖1:𝐴𝑖1)⋯(𝑥𝑖𝑘𝑖:𝐴𝑖𝑘𝑖)⊢𝐺𝑖. Its identity occurrence in Γ𝑖 is ?𝑚𝑖𝑥𝑖1⋯𝑥𝑖𝑘𝑖. A proof state for 𝑔 is 𝑆=(?𝑚1:𝑔1,…,?𝑚𝑛:𝑔𝑛;𝑉), where the ?𝑚𝑖 are pairwise distinct, there is exactly one declaration for each open goal, and 𝑉 is a validation from those goals to 𝑔. The state represents the suspended meta-expression ⌈𝑆⌉:=𝑉(?𝑚1𝑥11⋯𝑥1𝑘1,…,?𝑚𝑛𝑥𝑛1⋯𝑥𝑛𝑘𝑛). Each argument in this display is formed in its own goal context before the validation abstracts or otherwise assembles it in Γ. No metavariable other than the displayed ?𝑚𝑖 may occur in ⌈𝑆⌉. The empty state (;𝑉) is successful: its suspended expression is the ordinary kernel term 𝑉().
The validation clause is the LCF invariant. It is stronger than saying that every subgoal looks plausible: it states how proofs of all subgoals assemble to a proof of the original goal.
A tactic maps a goal either to failure or to a proof state for that goal. The following primitive tactics are defined when their displayed side conditions hold.
𝖾𝗑𝖺𝖼𝗍(𝑞) on (Γ∣𝐺) returns the empty state with validation ()↦𝑞, provided the kernel derives Γ⊢𝑞:𝐺.
𝗂𝗇𝗍𝗋𝗈 on (Γ∣∏𝑥:𝐴𝐵) returns (?𝑚:(Γ,𝑥:𝐴∣𝐵);𝑝↦𝜆𝑥.𝑝), where ?𝑚 is fresh.
𝗌𝗉𝗅𝗂𝗍 on (Γ∣𝐴×𝐵) returns the goals explicitly represented by (?𝑚:(Γ∣𝐴),?𝑛:(Γ∣𝐵);(𝑝,𝑞)↦(𝑝,𝑞)), where ?𝑚 and ?𝑛 are fresh and distinct.
𝖺𝗌𝗌𝗎𝗆𝗉𝗍𝗂𝗈𝗇 returns 𝖾𝗑𝖺𝖼𝗍(𝑥) for the first declaration 𝑥:𝐺 in Γ, and fails if there is none.
The ordering in the last clause is part of the replayable tactic semantics.
For the opening goal, the first 𝗂𝗇𝗍𝗋𝗈 returns (?𝑚1:(𝑥:𝐴∣𝐵→𝐴);𝑝↦𝜆𝑥.𝑝),⌈𝑆1⌉=𝜆𝑥.?𝑚1𝑥. Applying 𝗂𝗇𝗍𝗋𝗈 to its one open goal and composing validations returns (?𝑚2:(𝑥:𝐴,𝑦:𝐵∣𝐴);𝑞↦𝜆𝑥.𝜆𝑦.𝑞),⌈𝑆2⌉=𝜆𝑥.𝜆𝑦.?𝑚2𝑥𝑦. The assumption tactic selects 𝑥:𝐴 and directly supplies it as the proof argument for ?𝑚2. The resulting empty state has suspended term 𝜆𝑥.𝜆𝑦.𝑥. Each transition performs an actual meta-level substitution into a validation; none is a kernel reduction rule.
Proof. For 𝖾𝗑𝖺𝖼𝗍(𝑞), the side condition is precisely the conclusion required of its nullary validation. For 𝗂𝗇𝗍𝗋𝗈, inversion of the goal-formation judgment gives Γ,𝑥:𝐴𝖼𝗍𝗑 and Γ,𝑥:𝐴⊢𝐵𝗍𝗒𝗉𝖾, so its contextual-metavariable declaration is well formed. Assuming Γ,𝑥:𝐴⊢𝑝:𝐵, the Π-introduction rule gives Γ⊢𝜆𝑥.𝑝:∏𝑥:𝐴𝐵. For 𝗌𝗉𝗅𝗂𝗍, inversion of product formation makes both displayed metavariable declarations well formed. The two assumptions Γ⊢𝑝:𝐴 and Γ⊢𝑞:𝐵 give Γ⊢(𝑝,𝑞):𝐴×𝐵 by product introduction. The assumption case reduces to the exact case because the variable rule derives Γ⊢𝑥:𝐺. ◻
★☆☆ Let 𝑓:𝐴→𝐵 occur in Γ. Define a primitive tactic for the goal (Γ∣𝐵) that creates the single goal (Γ∣𝐴). Give its validation and prove its validity in two lines.
Applying a tactic only to the first generated goal loses the remaining validation arguments. Sequencing must therefore distribute a second tactic over every subgoal and compose all validations.
Write 𝑇@𝑔⇓𝑆 when tactic 𝑇 succeeds on goal 𝑔 with proof state 𝑆, and write 𝖿𝖺𝗂𝗅𝗌(𝑇,𝑔) for failure. The four primitives have exactly the successful results in definition 114.2. Their failure rules are
Γ⊬𝑞:𝐺
𝖿𝖺𝗂𝗅𝗌(𝖾𝗑𝖺𝖼𝗍(𝑞),(Γ∣𝐺))
Tac-Exact-Fail
¬∃𝑥,𝐴,𝐵.𝐺=∏𝑥:𝐴𝐵
𝖿𝖺𝗂𝗅𝗌(𝗂𝗇𝗍𝗋𝗈,(Γ∣𝐺))
Tac-Intro-Fail
¬∃𝐴,𝐵.𝐺=𝐴×𝐵
𝖿𝖺𝗂𝗅𝗌(𝗌𝗉𝗅𝗂𝗍,(Γ∣𝐺))
Tac-Split-Fail
nodeclaration𝑥:𝐺occursinΓ
𝖿𝖺𝗂𝗅𝗌(𝖺𝗌𝗌𝗎𝗆𝗉𝗍𝗂𝗈𝗇,(Γ∣𝐺))
Tac-Assumption-Fail
The first premise means that the kernel rejects the exact judgment; it is not a second typing relation. The two shape failures are syntactic because the primitive success clauses in definition 114.2 match those two outer forms; a front end may normalize a goal before invoking a primitive. For a vector of goals ⃗ℎ𝑖, write ―――?𝑛𝑖:⃗ℎ𝑖 for a vector containing one fresh contextual metavariable declaration for each component of ⃗ℎ𝑖. Metavariable vectors generated by separate premises are chosen disjointly. Given 𝑉 and 𝑊1,…,𝑊𝑛, define their composite validation by 𝑉⃗𝑊(⃗𝑞1,…,⃗𝑞𝑛):=𝑉(𝑊1(⃗𝑞1),…,𝑊𝑛(⃗𝑞𝑛)). The rules for sequence and left-biased choice are
The sequence rule removes the intermediate declarations ?𝑚𝑖:𝑔𝑖: each 𝑊𝑖 directly supplies the corresponding argument of 𝑉. Its result contains exactly the final declarations ―――?𝑛𝑖:⃗ℎ𝑖, and splits their proof arguments according to the displayed concatenation. If 𝑇 succeeds on no goals, Tac-Seq has no 𝑈-premises and returns the same nullary validation.
For repetition, write a state relative to its original goal as 𝑆=(?𝑚1:𝑔1,…,?𝑚𝑛:𝑔𝑛;𝑉). Put 𝜇((;𝑉)):=(0,0),𝜇((?𝑚1:𝑔1,…,?𝑚𝑛:𝑔𝑛;𝑉)):=(1+𝑐(𝑔1),𝑛)(𝑛>0), ordered lexicographically, where 𝑐(𝑔1) counts → and × in the conclusion of 𝑔1. Thus the empty state is below every nonempty state, including a single atomic goal. Suppose 𝑇@𝑔1⇓(―――?𝑛:⃗ℎ;𝑊), where every declaration in ―――?𝑛:⃗ℎ is fresh for the declarations retained from 𝑆. Define 𝑆′=(―――?𝑛:⃗ℎ,?𝑚2:𝑔2,…,?𝑚𝑛:𝑔𝑛;𝑉′),𝑉′(⃗𝑞,𝑝2,…,𝑝𝑛):=𝑉(𝑊(⃗𝑞),𝑝2,…,𝑝𝑛). A one-step transition on the first goal is then
𝑇@𝑔1⇓(―――?𝑛:⃗ℎ;𝑊)𝜇(𝑆′)<𝜇(𝑆)
𝑆𝗌𝗍𝖾𝗉𝑇𝑆′
Rep-Step
Define 𝗌𝗍𝗈𝗉𝗌𝑇(𝑆) to hold exactly when 𝑆 is empty, or its first goal 𝑔1 satisfies 𝖿𝖺𝗂𝗅𝗌(𝑇,𝑔1), or every successful evaluation of 𝑇 on 𝑔1 produces a state whose measure is not below 𝜇(𝑆). The terminating closure is generated by
𝗌𝗍𝗈𝗉𝗌𝑇(𝑆)
𝖱𝖾𝗉𝑇(𝑆,𝑆)
Rep-Done
𝑆𝗌𝗍𝖾𝗉𝑇𝑆1𝖱𝖾𝗉𝑇(𝑆1,𝑆2)
𝖱𝖾𝗉𝑇(𝑆,𝑆2)
Rep-More
Thus Rep-Done is available exactly at a stopping state. For a fresh contextual metavariable ?𝑚:𝑔, the identity state has suspended expression ?𝑚 applied to the variables of the context of 𝑔. Finally, 𝗋𝖾𝗉𝖾𝖺𝗍(𝑇)@𝑔⇓𝑆′⟺𝖱𝖾𝗉𝑇((?𝑚:𝑔;𝑝↦𝑝),𝑆′). Thus choice and repetition fix both the returned proof term and its replay trace; the decrease side condition excludes 𝗋𝖾𝗉𝖾𝖺𝗍(𝗂𝖽).
Let 𝑇 be generated from the primitives in definition 114.2 by the tacticals in definition 114.4. If 𝑇 succeeds on 𝑔=(Γ∣𝐺) with the empty state, then its nullary validation returns a term 𝑝 satisfying Γ⊢𝑝:𝐺.
Proof. We prove the stronger claim that every successful evaluation returns a proof state valid for its input goal, by structural induction on the construction of the tactic. For a fixed tactic constructor, we then invert its successful evaluation rule. The four primitive constructors are lemma 114.3; the structural induction hypotheses are available for each proper sub-tactic of sequencing, choice, and repetition.
In Tac-Seq, suppose the first evaluation returns 𝑉, and the evaluations on its subgoals return 𝑊𝑖. Given kernel proofs for every final goal, validity of 𝑊𝑖 gives a proof of 𝑔𝑖. Validity of 𝑉 then gives a proof of 𝑔, exactly as the composite in the conclusion of Tac-Seq. The output declarations are exactly the fresh declarations generated by the 𝑊𝑖, so every final goal is represented once and no intermediate ?𝑚𝑖 remains in the suspended expression. The two sequence-failure rules have no successful conclusion. In Tac-Or-Left use the induction hypothesis for 𝑇; in Tac-Or-Right use it for 𝑈. Failure of both branches likewise has no successful conclusion.
For repetition, fix the structural induction hypothesis that every successful evaluation of the proper sub-tactic 𝑇 returns a valid state. Prove the following two claims simultaneously by rule induction on the displayed derivations:
if 𝑆𝗌𝗍𝖾𝗉𝑇𝑆1 and 𝑆 is valid for its original goal, then 𝑆1 is valid for that goal;
if 𝖱𝖾𝗉𝑇(𝑆,𝑆′) and 𝑆 is valid for its original goal, then 𝑆′ is valid for that goal.
The sole case of the first claim is Rep-Step. The structural induction hypothesis makes 𝑊 a validation of (ℎ1,…,ℎ𝑘) to 𝑔1. Substituting that validation into the first argument of 𝑉 gives exactly 𝑉′. Freshness of the declarations generated by 𝑊 and retention of exactly the declarations for 𝑔2,…,𝑔𝑛 give the required one-to-one goal representation, hence validity of the target state. For the second claim, Rep-Done returns the same state. In Rep-More, claim 1 gives validity of 𝑆1, and the induction hypothesis for its 𝖱𝖾𝗉𝑇(𝑆1,𝑆2) premise gives validity of 𝑆2. These are all rules of the mutually established step/closure invariant. The strict decrease in Rep-Step, using 𝜇((;𝑉))=(0,0) at the empty boundary, also proves that no infinite chain of Rep-More premises exists. Applying claim 2 to the initial identity state proves the repeat case. These are all successful evaluation-rule families. For an empty result list, validity of the resulting proof state is precisely the theorem’s conclusion. ◻
A generated metavariable is the explicit representative of an open goal, not an unscoped hole. General occurrences need not be identity occurrences: if ?𝑚:(𝑥1:𝐴1)⋯(𝑥𝑘:𝐴𝑘)⊢𝐵, then ?𝑚𝑢1⋯𝑢𝑘 is formed only when the kernel checks the arguments in telescope order.
Let 𝑆=(?𝑚1:𝑔1,…,?𝑚𝑛:𝑔𝑛;𝑉), where 𝑔𝑖=(Γ𝑖∣𝐺𝑖). A meta-substitution 𝜌closes𝑆 when both conditions hold:
its domain is exactly {?𝑚1,…,?𝑚𝑛};
for every 𝑖, its assignment 𝜌(?𝑚𝑖)=𝑞𝑖 is a metavariable-free kernel term satisfying Γ𝑖⊢𝑞𝑖:𝐺𝑖;
Contextual substitution acts on an occurrence by (?𝑚𝑖𝑢1⋯𝑢𝑘𝑖)[𝜌]:=𝑞𝑖[𝑢1/𝑥𝑖1,…,𝑢𝑘𝑖/𝑥𝑖𝑘𝑖], and acts pointwise on a validation application: 𝑉(𝑡1,…,𝑡𝑛)[𝜌]:=𝑉(𝑡1[𝜌],…,𝑡𝑛[𝜌]). Thus the empty substitution closes only an empty state; a state with an open goal cannot satisfy closure without a checked proof of that goal.
For the state 𝑆2 in the opening calculation, put 𝜌2(?𝑚2)=𝑥. The variable rule gives 𝑥:𝐴,𝑦:𝐵⊢𝑥:𝐴, so 𝜌2 closes 𝑆2, and ⌈𝑆2⌉[𝜌2]=𝜆𝑥.𝜆𝑦.𝑥. The empty substitution does not close 𝑆2, because its domain omits ?𝑚2.
An unscoped placeholder ?𝑚:𝐵 would allow a tactic to construct ?𝑚𝑥 after leaving the scope of 𝑥. The contextual declaration prevents that term from being formed unless 𝑥 is an explicit telescope argument.
Proof of Proposition 114.7 — Closure removes tactic state
Proof. Write 𝑆=(?𝑚1:𝑔1,…,?𝑚𝑛:𝑔𝑛;𝑉). By closure, 𝜌(?𝑚𝑖)=𝑞𝑖 and Γ𝑖⊢𝑞𝑖:𝐺𝑖 for every 𝑖. The definition of the suspended expression and contextual substitution give ⌈𝑆⌉[𝜌]=𝑉(𝑞1,…,𝑞𝑛). The defining property of the validation therefore gives Γ⊢𝑉(𝑞1,…,𝑞𝑛):𝐺. The proof-state definition says that the only metavariables in ⌈𝑆⌉ are the ?𝑚𝑖, and closure makes every 𝑞𝑖 metavariable-free. Hence no metavariable occurs in 𝑝. Specializing the displayed calculation to 𝑛=0 yields 𝑝=𝑉(), as required. ◻
Case splitting and deterministic replay
For an inductive family, a case split cannot guess a nondependent return type. It must expose a motive.
For 𝑠:𝐼⃗𝑎⃗𝑖 and goal 𝐺, a case-split certificate contains a well-typed motive 𝑃:(⃗𝑗:⃗𝐽)→𝐼⃗𝑎⃗𝑗→U𝑘,𝑃⃗𝑖𝑠≡𝐺, where 𝑘 is the level at which the kernel derives Γ⊢𝐺:U𝑘, and ⃗𝐽 is the index telescope of 𝐼 at the parameters ⃗𝑎; one fresh contextual-metavariable declaration for every constructor goal accepted by the family eliminator; and a validation whose eliminator application consumes exactly those branch-proof arguments. Impossible branches have no declaration and no validation argument, and are omitted only when the kernel derives the corresponding index contradiction.
For vectors, splitting 𝑣:𝖵𝖾𝖼𝐴(𝗌𝗎𝖼𝑛) creates only the fresh declaration for the 𝖼𝗈𝗇𝗌 branch, and the eliminator validation supplies its identity occurrence at that branch. There is no declaration for a 𝗇𝗂𝗅 branch. Deleting the successor index from the motive would also create an unprovable nil branch. Thus the motive and the exact branch declarations are proof data rather than search metadata.
A replay certificate records the original goal, the ordered primitive calls, every generated contextual-metavariable declaration, every choice branch, every case-split motive, and the final closed proof term. Replay executes the recorded primitive calls without search and then invokes the kernel on the final term.
If deterministic replay accepts a certificate for (Γ∣𝐺), then the kernel accepts its final term 𝑝 at 𝐺. Corruption of a tactic trace cannot make the kernel accept an ill-typed 𝑝.
Proof. Replay acceptance includes the independent final call to the kernel checker. By checker soundness, that call returns a derivation of Γ⊢𝑝:𝐺. The trace is used to reproduce 𝑝, but is not a premise of the kernel derivation. Hence altering the trace either reproduces the same well-typed term, produces another term that the kernel checks, or is rejected. ◻
The abstract-theorem architecture and validations follow Cambridge LCF as described by Paulson, Technical Report 39, Section 2 for goals and validations and Section 5 for the tacticals [Pau83], and Gordon’s modern reconstruction [Gor15]. Those sources describe LCF; the soundness theorem above belongs to the finite calculus defined here.
Suggested first pass.
None of these problems is a prerequisite for a later chapter. Begin with exercise 114.2, then complete exercise 114.4.
★★☆ Evaluate 𝗂𝗇𝗍𝗋𝗈;(𝗌𝗉𝗅𝗂𝗍;𝖺𝗌𝗌𝗎𝗆𝗉𝗍𝗂𝗈𝗇) on 𝐴→(𝐴×𝐴). List every contextual-metavariable declaration and suspended expression in every proof state, then calculate the composite validation to the closed proof term.
★★☆ Construct two left-biased choices whose branches both prove 𝐴→𝐴 but return beta-distinct terms before normalization. Show that the branch order changes the replay trace while kernel soundness is unchanged.
★★★Practical project.proof-producing-tactic-replayer Implement in Agda or Kappa the propositional fragment with atoms, implication, and products, together with 𝗂𝗇𝗍𝗋𝗈, 𝗌𝗉𝗅𝗂𝗍, 𝖺𝗌𝗌𝗎𝗆𝗉𝗍𝗂𝗈𝗇, sequencing, and left-biased choice. Maintain the invariant that every open goal has exactly one contextual-metavariable declaration and is supplied as exactly one validation argument. On the named input k, print accepted: lambda-lambda-var. On dup, print accepted: pair-var-var. On bad-drop, print rejected: missing-validation-argument. Mutating sequence to discard the second generated goal must fail the last oracle. The program checks the finite tactic calculus; it does not prove soundness of a product tactic API.