diff --git a/CHANGES.txt b/CHANGES.txt
index 54def0ad4..aa29180f5 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -160,7 +160,13 @@ RELEASE  VERSION/DATE TO BE FILLED IN LATER
       variable names are given.
     - Update Clean and NoClean documentation.
     - Make sure unknown variables from a Variables file are recognized
-      as such (issue #4645)
+      as such. Previously only unknowns from the command line were
+      recognized (issue #4645).
+    - A Variables object now makes available a "defaulted" attribute,
+      a list of variable names that were set in the environment with
+      their values taken from the default in the variable description
+      (if a variable was set to the same value as the default in one
+      of the input sources, it is not included in this list).
 
 
 RELEASE 4.8.1 -  Tue, 03 Sep 2024 17:22:20 -0700
diff --git a/RELEASE.txt b/RELEASE.txt
index af5fb2fd2..aa3e3c15d 100644
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -76,6 +76,12 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
   always returns  a dict. The default remains to return different
   types depending on whether zero, one, or multiple construction
 
+- A Variables object now makes available a "defaulted" attribute,
+  a list of variable names that were set in the environment with
+  their values taken from the default in the variable description
+  (if a variable was set to the same value as the default in one
+  of the input sources, it is not included in this list).
+
 FIXES
 -----
 
diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py
index 80c4d1139..bc981e06f 100644
--- a/SCons/Variables/VariablesTests.py
+++ b/SCons/Variables/VariablesTests.py
@@ -659,24 +659,28 @@ def test_AddOptionUpdatesUnknown(self) -> None:
         Get one unknown from args and one from a variables file.
         Add these later, making sure they no longer appear in unknowns
         after the subsequent Update().
+
+        While we're here, test the *defaulted* attribute.
         """
         test = TestSCons.TestSCons()
         var_file = test.workpath('vars.py')
         test.write('vars.py', 'FROMFILE="added"')
         opts = SCons.Variables.Variables(files=var_file)
-        opts.Add('A', 'A test variable', "1")
+        opts.Add('A', 'A test variable', default="1")
+        opts.Add('B', 'Test variable B', default="1")
         args = {
             'A'             : 'a',
             'ADDEDLATER'    : 'notaddedyet',
         }
         env = Environment()
-        opts.Update(env,args)
+        opts.Update(env, args)
 
         r = opts.UnknownVariables()
         with self.subTest():
             self.assertEqual('notaddedyet', r['ADDEDLATER'])
             self.assertEqual('added', r['FROMFILE'])
             self.assertEqual('a', env['A'])
+            self.assertEqual(['B'], opts.defaulted)
 
         opts.Add('ADDEDLATER', 'An option not present initially', "1")
         opts.Add('FROMFILE', 'An option from a file also absent', "1")
@@ -693,6 +697,7 @@ def test_AddOptionUpdatesUnknown(self) -> None:
             self.assertEqual('added', env['ADDEDLATER'])
             self.assertNotIn('FROMFILE', r)
             self.assertEqual('added', env['FROMFILE'])
+            self.assertEqual(['B'], opts.defaulted)
 
     def test_AddOptionWithAliasUpdatesUnknown(self) -> None:
         """Test updating of the 'unknown' dict (with aliases)"""
diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py
index 1826c6444..de26e7b65 100644
--- a/SCons/Variables/__init__.py
+++ b/SCons/Variables/__init__.py
@@ -27,6 +27,7 @@
 
 import os.path
 import sys
+from contextlib import suppress
 from functools import cmp_to_key
 from typing import Callable, Sequence
 
@@ -58,22 +59,30 @@ class Variable:
     __slots__ = ('key', 'aliases', 'help', 'default', 'validator', 'converter', 'do_subst')
 
     def __lt__(self, other):
-        """Comparison fuction so Variable instances sort."""
+        """Comparison fuction so :class:`Variable` instances sort."""
         return self.key < other.key
 
     def __str__(self) -> str:
-        """Provide a way to "print" a Variable object."""
+        """Provide a way to "print" a :class:`Variable` object."""
         return (
-            f"({self.key!r}, {self.aliases}, {self.help!r}, {self.default!r}, "
+            f"({self.key!r}, {self.aliases}, "
+            f"help={self.help!r}, default={self.default!r}, "
             f"validator={self.validator}, converter={self.converter})"
         )
 
 
 class Variables:
-    """A container for multiple Build Variables.
+    """A container for Build Variables.
 
-    Includes methods to updates the environment with the variables,
-    and to render the help text.
+    Includes a method to populate the variables with values into a
+    construction envirionment, and methods to render the help text.
+
+    Note that the pubic API for creating a ``Variables`` object is
+    :func:`SCons.Script.Variables`, a kind of factory function, which
+    defaults to supplying the contents of :attr:`~SCons.Script.ARGUMENTS`
+    as the *args* parameter if it was not otherwise given. That is the
+    behavior documented in the manpage for ``Variables`` - and different
+    from the default if you instantiate this directly.
 
     Arguments:
       files: string or list of strings naming variable config scripts
@@ -83,11 +92,15 @@ class Variables:
          instead of a fresh instance. Currently inoperable (default ``False``)
 
     .. versionchanged:: 4.8.0
-       The default for *is_global* changed to ``False`` (previously
-       ``True`` but it had no effect due to an implementation error).
+       The default for *is_global* changed to ``False`` (the previous
+       default ``True`` had no effect due to an implementation error).
 
     .. deprecated:: 4.8.0
        *is_global* is deprecated.
+
+    .. versionadded:: NEXT_RELEASE
+       The :attr:`defaulted` attribute now lists those variables which
+       were filled in from default values.
     """
 
     def __init__(
@@ -102,15 +115,18 @@ def __init__(
             files = [files] if files else []
         self.files: Sequence[str] = files
         self.unknown: dict[str, str] = {}
+        self.defaulted: list[str] = []
 
     def __str__(self) -> str:
-        """Provide a way to "print" a Variables object."""
-        s = "Variables(\n  options=[\n"
-        for option in self.options:
-            s += f"    {str(option)},\n"
-        s += "  ],\n"
-        s += f"  args={self.args},\n  files={self.files},\n  unknown={self.unknown},\n)"
-        return s
+        """Provide a way to "print" a :class:`Variables` object."""
+        opts = ',\n'.join((f"    {option!s}" for option in self.options))
+        return (
+            f"Variables(\n  options=[\n{opts}\n  ],\n"
+            f"  args={self.args},\n"
+            f"  files={self.files},\n"
+            f"  unknown={self.unknown},\n"
+            f"  defaulted={self.defaulted},\n)"
+        )
 
     # lint: W0622: Redefining built-in 'help'
     def _do_add(
@@ -122,7 +138,7 @@ def _do_add(
         converter: Callable | None = None,
         **kwargs,
     ) -> None:
-        """Create a Variable and add it to the list.
+        """Create a :class:`Variable` and add it to the list.
 
         This is the internal implementation for :meth:`Add` and
         :meth:`AddVariables`. Not part of the public API.
@@ -203,9 +219,9 @@ def Add(
         return self._do_add(key, *args, **kwargs)
 
     def AddVariables(self, *optlist) -> None:
-        """Add a list of Build Variables.
+        """Add Build Variables.
 
-        Each list element is a tuple/list of arguments to be passed on
+        Each *optlist* element is a sequence of arguments to be passed on
         to the underlying method for adding variables.
 
         Example::
@@ -223,13 +239,22 @@ def AddVariables(self, *optlist) -> None:
     def Update(self, env, args: dict | None = None) -> None:
         """Update an environment with the Build Variables.
 
+        Collects variables from the input sources which do not match
+        a variable description in this object. These are ignored for
+        purposes of adding to *env*, but can be retrieved using the
+        :meth:`UnknownVariables` method.  Also collects variables which
+        are set in *env* from the default in a variable description and
+        not from the input sources. These are available in the
+        :attr:`defaulted` attribute.
+
         Args:
             env: the environment to update.
             args: a dictionary of keys and values to update in *env*.
                If omitted, uses the saved :attr:`args`
         """
-        # first pull in the defaults
+        # first pull in the defaults, except any which are None.
         values = {opt.key: opt.default for opt in self.options if opt.default is not None}
+        self.defaulted = list(values)
 
         # next set the values specified in any options script(s)
         for filename in self.files:
@@ -256,6 +281,8 @@ def Update(self, env, args: dict | None = None) -> None:
                     for option in self.options:
                         if arg in option.aliases + [option.key,]:
                             values[option.key] = value
+                            with suppress(ValueError):
+                                self.defaulted.remove(option.key)
                             added = True
                     if not added:
                         self.unknown[arg] = value
@@ -269,6 +296,8 @@ def Update(self, env, args: dict | None = None) -> None:
             for option in self.options:
                 if arg in option.aliases + [option.key,]:
                     values[option.key] = value
+                    with suppress(ValueError):
+                        self.defaulted.remove(option.key)
                     added = True
             if not added:
                 self.unknown[arg] = value
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 8366bbd92..1b7e886c6 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -4704,47 +4704,52 @@ env = conf.Finish()
 <refsect2 id='commandline_construction_variables'>
 <title>Command-Line Construction Variables</title>
 
-<para>Often when building software,
-specialized information needs to be conveyed at build time
-to override the defaults in the build scripts.
-Command-line arguments (like <literal>--implcit-cache</literal>)
-and giving names of build targets are two ways to do that.
-Another is to provide variable-assignment arguments
-on the command line.
-For the particular case where you want to specify new
+<para>
+&SCons; depends on information stored in &consvars; to
+control how targets are built.
+It is often necessary to pass
+specialized information at build time
+to override the variables in the build scripts.
+This can be done through variable-assignment arguments
+on the command line and/or in stored variable files.
+</para>
+
+<para>
+For the case where you want to specify new
 values for &consvars;,
 &SCons; provides a <firstterm>&Variables;</firstterm>
 object to simplify collecting those
 and updating a &consenv; with the values.
-The typical calling style looks like:
+This helps processing commands lines like this:
 </para>
 
 <screen>
-<userinput>scons VARIABLE=foo</userinput>
+<userinput>scons VARIABLE=foo OTHERVAR=bar</userinput>
 </screen>
 
 <para>
-Variables specified in the above way
-can be manually processed by accessing the
+Variables supplied on the command line
+can always be manually processed by iterating the
 <link linkend="v-ARGUMENTS">&ARGUMENTS;</link> dictionary
-(or <link linkend="v-ARGLIST">&ARGLIST;</link> list),
-but using a &Variables; object allows you to describe
+or the <link linkend="v-ARGLIST">&ARGLIST;</link> list,
+However, using a &Variables; object allows you to describe
 anticipated variables,
-convert them to a suitable type if necessary,
-validate the values are within defined constraints,
-and define defaults, help messages and aliases.
-This is conceptually similar to the structure of options
+perform necessary type conversion,
+validate that values meet defined constraints,
+and specify default values, help messages and aliases.
+This provides a somewhat similar interface to option handling
 (see &f-link-AddOption;).
-It also allows obtaining values from a saved variables file,
+A &Variables; object also allows
+obtaining values from a saved variables file,
 or from a custom dictionary in an &SConscript; file.
 The processed variables can then be applied to the desired &consenv;.
 </para>
 
 <para>
-Roughly speaking, arguments are used to convey information to the
-&SCons; program about how it should behave;
-variables are used to convey information to the build
-(although &SCons; does not enforce any such constraint).
+Conceptually, command-line targets control what to build,
+command-line variables (and variable files) control how to build,
+and command-line options control how &SCons; operates
+(although &SCons; does not enforce that separation).
 </para>
 
 <para>To obtain an object for manipulating variables,
@@ -4755,23 +4760,23 @@ call the &Variables; factory function:</para>
   <term><function>Variables</function>([<parameter>files, [args]]</parameter>)</term>
   <listitem>
 <para>If <parameter>files</parameter> is a filename or list of filenames,
-they are considered to be &Python; scripts which will
-be executed to set variables when the
+they are executed as &Python; scripts
+to set saved variables when the
 <link linkend='v-Update'><function>Update</function></link>
-method is called -
-this allows the use of &Python; syntax in the assignments.
-A file can be the result of an earlier call to the
+method is called.
+This allows the use of &Python; syntax in the assignments.
+A variables file can be the result of an previous call to the
 <link linkend='v-Save'>&Save;</link> method.
 If <parameter>files</parameter> is not specified,
 or the
 <parameter>files</parameter>
 argument is
 <constant>None</constant>,
-then no files will be read.
+then no files will be processed.
 Supplying <constant>None</constant> is required
 if there are no files but you want to specify
 <parameter>args</parameter> as a positional argument;
-this can be omitted if using the keyword argument style.
+or you can use keyword arguments to avoid that.
 If any of <parameter>files</parameter> is missing,
 it is silently skipped.
 </para>
@@ -4822,25 +4827,42 @@ vars = Variables(files=None, args=ARGUMENTS)
 </variablelist>
 
 <para>
-A &Variables; object serves as a container for
-descriptions of variables,
-which are added by calling methods of the object.
-Each variable consists of a name (which will
-become a &consvar;), aliases for the name,
+A &Variables; object is a container for variable descriptions,
+added by calling the
+<link linkend='v-Add'><function>Add</function></link> or
+<link linkend='v-AddVariables'><function>AddVariables</function></link>
+methods.
+Each variable description consists of a name (which will
+be used as the &consvar; name), aliases for the name,
 a help message, a default value,
 and functions to validate and convert values.
-Once the object is asked to process variables,
-it matches up data from the input
-sources it was given with the definitions,
-and generates key-value pairs which are added
-to the specified &consenv;,
-except that if any variable was described
-to have a default of <literal>None</literal>,
-it is <emphasis>not</emphasis> added to
-the construction environment unless it
-appears in the input sources.
-Otherwise, a variable not in the
-input sources is added using its default value.
+Processing of input sources
+is deferred until the
+<link linkend='v-Update'><function>Update</function></link>
+method is called,
+at which time the variables are added to the
+specified &consenv;.
+Variables from the input sources which do not match any
+names or aliases from the variable descriptions in this object are skipped,
+except that a dictionary of their names and values are made available
+in the <varname>.unknown</varname> attribute of the &Variables; object.
+This list can also be obtained via the
+<link linkend='v-UnknownVariables'><function>UnknownVariables</function></link>
+method.
+If a variable description has a default value
+other than <literal>None</literal> and does not
+appear in the input sources,
+it is added to the &consenv; with its default value.
+A list of variables set from their defaults and
+not supplied a value in the input sources is
+available as the <varname>.defaulted</varname> attribute
+of the &Variables; object.
+The unknown variables and defaulted information is
+not available until the &Update; method has run.
+</para>
+
+<para><emphasis>New in NEXT_RELEASE</emphasis>:
+the <parameter>defaulted</parameter> attribute.
 </para>
 
 <para>
@@ -4848,7 +4870,8 @@ Note that since the variables are eventually added as &consvars;,
 you should choose variable names which do not unintentionally change
 pre-defined &consvars; that your project will make use of
 (see <xref linkend="construction_variables"/> for a reference),
-since variables obtained have values overridden, not merged.
+since the specified values are assigned, not merged,
+to the respective &consvars;.
 </para>
 
 <para>
@@ -5037,7 +5060,9 @@ variables that were specified in the
 <parameter>files</parameter> and/or
 <parameter>args</parameter> parameters
 when <link linkend='v-Variables'>&Variables;</link>
-was called, but the object was not actually configured for.
+was called, but which were not configured in the object.
+The same dictionary is also available as the
+<varname>unknown</varname> attribute of the object.
 This information is not available until the
 <link linkend='v-Update'><function>Update</function></link>
 method has run.
@@ -5148,7 +5173,7 @@ vars.FormatVariableHelpText = my_format
 &SCons; provides five pre-defined variable types,
 accessible through factory functions that generate
 a tuple appropriate for directly passing to the
-<link linkend='v-Add'><function>Add</function></link>
+<link linkend='v-Add'><function>Add</function></link> or
 <link linkend='v-AddVariables'><function>AddVariables</function></link>
 methods.
 </para>
@@ -5191,7 +5216,7 @@ as false.</para>
   <listitem>
 <para>
 Set up a variable named <parameter>key</parameter>
-whose value may only be from
+whose value will be a choice from
 a specified list ("enumeration") of values.
 The variable will have a default value of
 <parameter>default</parameter>
@@ -5234,8 +5259,8 @@ converted to lower case.</para>
   <listitem>
 <para>
 Set up a variable named <parameter>key</parameter>
-whose value may be one or more
-from a specified list of values.
+whose value will be one or more
+choices from a specified list of values.
 The variable will have a default value of
 <parameter>default</parameter>,
 and <parameter>help</parameter>