diff --git a/cli/bulkseed.php b/cli/bulkseed.php
index 972855301f54629f77faa2d7a48d1c99ee6b88a7..eff13abf2f43a6dddba0eaa8d874cc1a5260aaf8 100755
--- a/cli/bulkseed.php
+++ b/cli/bulkseed.php
@@ -37,6 +37,7 @@ define('CLI_SCRIPT', true);
 require(__DIR__ . '/../../../../config.php');
 require_once($CFG->libdir.'/clilib.php');
 require_once($CFG->libdir . '/questionlib.php');
+require_once(__DIR__ . '/../vle_specific.php');
 require_once(__DIR__ . '/../locallib.php');
 require_once(__DIR__ . '/../stack/utils.class.php');
 require_once(__DIR__ . '/../stack/bulktester.class.php');
@@ -119,7 +120,7 @@ foreach ($questions as $id) {
         'category' => $questiondata->category,
         'lastchanged' => $id->id,
         'courseid' => cat_to_course($questiondata->category));
-    if ($questiondata->hidden) {
+    if (property_exists($questiondata, 'hidden') && $questiondata->hidden) {
         $urlparams['showhidden'] = 1;
     }
 
@@ -155,10 +156,7 @@ foreach ($questions as $id) {
         }
 
         // Prepare the display options.
-        $options = new question_display_options();
-        $options->readonly = true;
-        $options->flags = question_display_options::HIDDEN;
-        $options->suppressruntestslink = true;
+        $options = question_display_options();
         $question->castextprocessor = new castext2_qa_processor($quba->get_question_attempt($slot));
 
         // Create the question text, question note and worked solutions.
diff --git a/doc/en/Developer/Development_track.md b/doc/en/Developer/Development_track.md
index 3276e7e9b543b12e2875ff4a7834256e48af9bcd..f139f122269cd1667f5b5888345e8e3e2128bbd1 100644
--- a/doc/en/Developer/Development_track.md
+++ b/doc/en/Developer/Development_track.md
@@ -10,16 +10,13 @@ past development history is documented on [Development history](Development_hist
 1. Refactor the healthcheck scripts, especially to make unicode requirements for maxima more prominent.
 2. Allow users to [systematically deploy](../CAS/Systematic_deployment.md) all variants of a question in a simple manner.
 3. Tag inputs with 'aria-live' is 'assertive' for better screen reader support.
+4. Confirm support for PHP 8.2, (fixes issue #986).
 
 TODO: 
 
-1. Support for PHP 8.2.  See issue #986.
-  * Deprecated: Creation of dynamic property question_display_options::$suppressruntestslink is deprecated in /var/www/html/moodle42/question/type/stack/questiontestrun.php on line 127
-    The question_display_options class is a moodle issue.
-
-2. Fix markdown problems. See issue #420.
-3. Error messages: use caserror.class more fully to use user information to target error messages.
-4. Remove all "cte" code from Maxima - mostly install.
+1. Fix markdown problems. See issue #420.
+2. Error messages: use caserror.class more fully to use user information to target error messages.
+3. Remove all "cte" code from Maxima - mostly install.
 
 ## For "inputs 2"?
 
diff --git a/questiondisplayoptions.php b/questiondisplayoptions.php
new file mode 100644
index 0000000000000000000000000000000000000000..ba085bfded82f7b7279a7f9493db70104332e3e1
--- /dev/null
+++ b/questiondisplayoptions.php
@@ -0,0 +1,28 @@
+<?php
+// This file is part of Stack - http://stack.maths.ed.ac.uk/
+//
+// Stack is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Stack is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Stack.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Stack question display class.
+ * Required in PHP 8.2 to dynamically extend Moodle's class.
+ * This also moves a moodle dependency into the vle specific code area.
+ *
+ * @package   qtype_stack
+ * @copyright 2023 The University of Edinburgh
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_stack_question_display_options extends question_display_options {
+    public $suppressruntestslink = false;
+}
diff --git a/questiontestedit.php b/questiontestedit.php
index d64d992e567677ec77e937d8460aca1d111876fc..45333c8a5e26c062f1926f10cdf2dace014b1338 100644
--- a/questiontestedit.php
+++ b/questiontestedit.php
@@ -24,6 +24,7 @@
 require_once(__DIR__.'/../../../config.php');
 
 require_once($CFG->libdir . '/questionlib.php');
+require_once(__DIR__ . '/vle_specific.php');
 require_once(__DIR__ . '/locallib.php');
 require_once(__DIR__ . '/questiontestform.php');
 require_once(__DIR__ . '/stack/questiontest.php');
@@ -155,10 +156,7 @@ if ($mform->is_cancelled()) {
 }
 
 // Prepare the display options.
-$options = new question_display_options();
-$options->readonly = true;
-$options->flags = question_display_options::HIDDEN;
-$options->suppressruntestslink = true;
+$options = question_display_options();
 
 // Display the page.
 echo $OUTPUT->header();
diff --git a/questiontestrun.php b/questiontestrun.php
index 561bff39075822c411a8d00112dc123ec2bebb3c..9f194608df3f97ace6a76cb165cad94012d09a59 100644
--- a/questiontestrun.php
+++ b/questiontestrun.php
@@ -36,6 +36,7 @@ define('NO_OUTPUT_BUFFERING', true);
 require_once(__DIR__.'/../../../config.php');
 
 require_once($CFG->libdir . '/questionlib.php');
+require_once(__DIR__ . '/vle_specific.php');
 require_once(__DIR__ . '/locallib.php');
 require_once(__DIR__ . '/stack/questiontest.php');
 require_once(__DIR__ . '/stack/bulktester.class.php');
@@ -121,11 +122,7 @@ $slot = $quba->add_question($question, $question->defaultmark);
 $quba->start_question($slot);
 
 // Prepare the display options.
-$options = new question_display_options();
-$options->readonly = true;
-$options->flags = question_display_options::HIDDEN;
-$options->suppressruntestslink = true;
-
+$options = question_display_options();
 // Start output.
 echo $OUTPUT->header();
 $renderer = $PAGE->get_renderer('qtype_stack');
diff --git a/tidyquestion.php b/tidyquestion.php
index 7cd2a32980f796acf47384d55d556e40be5859bb..6994a093aa65877338e7992ff981f469aad98b82 100644
--- a/tidyquestion.php
+++ b/tidyquestion.php
@@ -23,7 +23,6 @@
  */
 
 require_once(__DIR__.'/../../../config.php');
-
 require_once($CFG->libdir . '/questionlib.php');
 require_once(__DIR__ . '/locallib.php');
 require_once(__DIR__ . '/tidyquestionform.php');
@@ -78,10 +77,7 @@ $quba->process_action($slot, $response);
 $question->setup_fake_feedback_and_input_validation();
 
 // Prepare the display options.
-$options = new question_display_options();
-$options->readonly = true;
-$options->flags = question_display_options::HIDDEN;
-$options->suppressruntestslink = true;
+$options = question_display_options();
 
 // Create the form for renaming bits of the question.
 $form = new qtype_stack_tidy_question_form($PAGE->url, $question);
diff --git a/vle_specific.php b/vle_specific.php
index 1f200af90118948716178bbfb2c8c6e60ba17072..d4db739788aa3cf87ca9197ec8b9d4b32f3e69e4 100644
--- a/vle_specific.php
+++ b/vle_specific.php
@@ -14,6 +14,11 @@
 // You should have received a copy of the GNU General Public License
 // along with Stack.  If not, see <http://www.gnu.org/licenses/>.
 
+defined('MOODLE_INTERNAL') || die();
+
+// This file defines question_display_options which the next class extends.
+require_once(__DIR__.'/../../../lib/questionlib.php');
+require_once('questiondisplayoptions.php');
 
 /**
  * A collection of things that are a bit VLE specific and have been
@@ -194,3 +199,14 @@ function stack_get_mathjax_url(): string {
 function stack_clear_vle_question_cache(int $questionid) {
     question_bank::notify_question_edited($questionid);
 }
+
+/*
+ * This is needed to put links to the STACK question dashboard into the question.
+ */
+function question_display_options() {
+    $options = new qtype_stack_question_display_options();
+    $options->readonly = true;
+    $options->flags = question_display_options::HIDDEN;
+    $options->suppressruntestslink = true;
+    return $options;
+}