This tool detects definitions of HOL Light tactics/conversions as well as their users, and patches proof files so that
- Tactics log the input and output goals
- Conversions log the input term and output theorem
in the JSON format.
Also, this tool provides a tool for collecting top-level theorems that are defined
as let <theorem> = prove(<goal>, <proof>);; and dumping in the JSON format.
This project does not need patching HOL Light.
Instead, HOL Light must be built with OCaml 5.4.0 (the make switch-5 as of
Sep. 16, 2026) and compiled with HOLLIGHT_USE_MODULE=1:
git clone https://github.com/jrh13/hol-light.git
cd hol-light
make switch-5
eval $(opam env)
HOLLIGHT_USE_MODULE=1 makeLocation.
TacticTrace's scripts locate each other, hol_lib_inlined.mli and
kernel_wrapper.ml through $HOLLIGHT_DIR/TacticTrace, so this repository has
to be checked out inside the HOL Light directory:
git clone https://github.com/hol-light/TacticTrace.git <the HOL Light dir>/TacticTraceOperating System. TacticTrace is tested on Ubuntu and MacOS, both of which are covered by continuous integration.
export HOLLIGHT_DIR=<the HOL Light dir>
eval $(opam env --set-switch --switch=${HOLLIGHT_DIR})
make
./build-hol-kernel.shThis will create kernel_wrapper.ml which will be used in the next step.
Let's assume that you want have a HOL Light proof file a.ml.
You will need to inline loadt/loads/needs invocations in a.ml through the following
OCaml script which is provided by HOL Light:
${HOLLIGHT_DIR}/hol.sh inline-load a.ml a_inlined.mlThe next step is to modify the definitions of tactics/conversions as well as their
users in a_inlined.ml so that they emit the inputs and outputs to a JSON file.
${HOLLIGHT_DIR}/TacticTrace/modify-proof.sh a_inlined.ml a_inlined_wrapped.ml /home/your/output/dirYou can run the a_inlined_wrapped.ml by loading it on top of HOL Light REPL (hol.sh), or
building it using the OCaml compiler. The following commands show how to build it using the OCaml
native compiler.
${HOLLIGHT_DIR}/hol.sh compile a_inlined_wrapped.ml -o a_inlined_wrapped.cmx
${HOLLIGHT_DIR}/hol.sh link a_inlined_wrapped.cmx -o a_inlined_wrapped.nativeGiven an inlined HOL Light proof a_inlined.ml, you can use tracer collect-top-level-thms to
collect information of the top-level theorems.
For example, if a_inlined.ml contains:
...
let MY_THM = prove(
`forall x, x + 1 = 1 + x`,
ARITH_TAC);;
Running the following commands will save a JSON file that contains the line number
informations of theorems including MY_THM.
${HOLLIGHT_DIR}/TacticTrace/get-ast.sh a_inlined.ml # This creates a_inlined.marshalled.bin
${HOLLIGHT_DIR}/TacticTrace/tracer collect-toplevel-thms a_inlined.marshalled.bin output.json
Correctness of line/column number information of the goal. If the goal term of a theorem consists of multiple lines, e.g.,
prove(`forall x,
x + 1 = 1 + x`,
(my tac))
You will observe that the column number and line number does not exactly match. This is because the source code preprocessor of HOL Light first replaces line breaks in a term with spaces, as follows:
prove(`forall x, x + 1 = 1 + x`,
(blank line)
(my tac))
Therefore, if you want to extract the string representation of goal from the source code,
goal_linenum_end and goal_colnum_end should be properly adjusted.
Theorems inside modules. TacticTrace will not catch tactics that are defined inside a module.
make test runs the proofs in examples/ through the full pipeline of steps 1
and 2 above, writing the collected traces to examples/<name>.outdir and the
HOL Light output to examples/<name>.hollog, and then compares the collected
traces against the expected traces in examples/<name>.answer:
export HOLLIGHT_DIR=<the HOL Light dir>
eval $(opam env --set-switch --switch=${HOLLIGHT_DIR})
make
./build-hol-kernel.sh
make testmake test fails if HOL Light was not built with HOLLIGHT_USE_MODULE=1, since
no traces can be collected without it.
The expected traces record the HOL Light directory as the literal string
$HOLLIGHT_DIR so that they do not depend on where HOL Light is checked out;
check-answers.sh substitutes the real path before comparing.
If you change the trace format, make test will report the difference and fail.
To accept the new traces as expected, run:
./check-answers.sh --update
make test--update writes the traces that the failing make test already collected in
examples/<name>.outdir over examples/<name>.answer, normalizing them the same
way the comparison does. Review the resulting diff before committing it, and run
make test again to confirm it now passes.
This is also what GitHub Actions runs, on Ubuntu and on MacOS; see
.github/workflows/. ci.yml builds against a fixed HOL Light revision, so
that a red build always means a change here broke something rather than upstream
moving; bump the revision there when TacticTrace is updated for a newer HOL
Light. upstream.yml builds against HOL Light master weekly, to give early
warning when upstream drifts away from us. It is kept separate so that it does
not appear among the checks on a pull request.
Releases are tagged ocaml-<version>/v<n>, where <version> is the OCaml
version that HOL Light must be built with and <n> counts the releases made
against it:
ocaml-5.4/v1
ocaml-5.4/v2
ocaml-5.4/v3
|
ocaml-5.5/v1
TacticTrace tracks OCaml and HOL Light internals closely enough that the
supported compiler is the most important thing a user needs to know about a
release, so it is named in the tag rather than left to a changelog. Updates for
the currently supported compiler bump <n>; when HOL Light supports a new OCaml
version well, that becomes a new ocaml-<version>/v1. The two series can be
maintained in parallel from release/ocaml-5.4-style branches if a fix is
needed for an older compiler after main has moved on.
TacticTrace was developed inside the HOL Light repository, as its TacticTrace
directory, from September 2025 until it moved to this repository. HOL Light's
holtest.mk used to test it through the TacticTrace/make-test target; that is
now covered by this repository's GitHub Actions workflows.
Juneyoung Lee, aqjune@gmail.com