<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://edfieldmanual.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AProcedures</id>
	<title>Module:Procedures - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://edfieldmanual.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AProcedures"/>
	<link rel="alternate" type="text/html" href="https://edfieldmanual.com/index.php?title=Module:Procedures&amp;action=history"/>
	<updated>2026-08-25T11:32:28Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.46.0</generator>
	<entry>
		<id>https://edfieldmanual.com/index.php?title=Module:Procedures&amp;diff=1691&amp;oldid=prev</id>
		<title>Sythan: Fix procedure directory Engineer metadata loading</title>
		<link rel="alternate" type="text/html" href="https://edfieldmanual.com/index.php?title=Module:Procedures&amp;diff=1691&amp;oldid=prev"/>
		<updated>2026-08-24T02:02:06Z</updated>

		<summary type="html">&lt;p&gt;Fix procedure directory Engineer metadata loading&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw-interface=&quot;&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 02:02, 24 August 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l37&quot;&gt;Line 37:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 37:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function loadData(title)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function loadData(title)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local ok, data = pcall(mw.loadData, title)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local ok, data = pcall(mw.loadData, title)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	if ok and type(data) == &#039;table&#039; then&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;		return data&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	end&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	return {}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;end&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;local function loadJsonPage(titleText)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	local title = mw.title.new(titleText)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	local content = title and title:getContent()&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	if not content then return {} end&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	local ok, data = pcall(mw.text.jsonDecode, content)&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	if ok and type(data) == &amp;#039;table&amp;#039; then&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	if ok and type(data) == &amp;#039;table&amp;#039; then&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		return data&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		return data&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l61&quot;&gt;Line 61:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 72:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function engineerProcedures()&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function engineerProcedures()&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local out = {}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	local out = {}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	for _, name in ipairs(&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;loadData&lt;/del&gt;(&#039;Module:Data/Engineers/index&#039;)) do&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	for _, name in ipairs(&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;loadJsonPage&lt;/ins&gt;(&#039;Module:Data/Engineers/index&#039;)) do&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		local record = &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;loadData&lt;/del&gt;(&#039;Module:Data/Engineers/&#039; .. name)&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		local record = &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;loadJsonPage&lt;/ins&gt;(&#039;Module:Data/Engineers/&#039; .. name)&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		local scope = record.engineer_scope or &amp;#039;ship&amp;#039;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		local scope = record.engineer_scope or &amp;#039;ship&amp;#039;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		local topic = scope == &amp;#039;odyssey&amp;#039; and &amp;#039;Odyssey / On-foot&amp;#039; or &amp;#039;Engineering&amp;#039;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		local topic = scope == &amp;#039;odyssey&amp;#039; and &amp;#039;Odyssey / On-foot&amp;#039; or &amp;#039;Engineering&amp;#039;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Sythan</name></author>
	</entry>
	<entry>
		<id>https://edfieldmanual.com/index.php?title=Module:Procedures&amp;diff=1602&amp;oldid=prev</id>
		<title>Sythan: Register Field Procedures metadata and Mining procedure placeholders</title>
		<link rel="alternate" type="text/html" href="https://edfieldmanual.com/index.php?title=Module:Procedures&amp;diff=1602&amp;oldid=prev"/>
		<updated>2026-08-23T23:31:02Z</updated>

		<summary type="html">&lt;p&gt;Register Field Procedures metadata and Mining procedure placeholders&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Module:Procedures&lt;br /&gt;
-- Canonical Field Procedure metadata and directory helpers.&lt;br /&gt;
-- Mining procedure records are stored in Module:Data/Procedures/Mining.&lt;br /&gt;
-- Engineer unlock procedures are derived from Module:Data/Engineers so the&lt;br /&gt;
-- Field Procedures directory does not duplicate the Engineer roster.&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local TOPIC_ORDER = {&lt;br /&gt;
	&amp;quot;Mining&amp;quot;,&lt;br /&gt;
	&amp;quot;Engineering&amp;quot;,&lt;br /&gt;
	&amp;quot;Odyssey / On-foot&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function isNonEmpty(value)&lt;br /&gt;
	return value ~= nil and value ~= &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function escapeAttribute(value)&lt;br /&gt;
	value = tostring(value or &amp;#039;&amp;#039;)&lt;br /&gt;
	value = value:gsub(&amp;#039;&amp;amp;&amp;#039;, &amp;#039;&amp;amp;amp;&amp;#039;)&lt;br /&gt;
	value = value:gsub(&amp;#039;&amp;quot;&amp;#039;, &amp;#039;&amp;amp;quot;&amp;#039;)&lt;br /&gt;
	value = value:gsub(&amp;#039;&amp;lt;&amp;#039;, &amp;#039;&amp;amp;lt;&amp;#039;)&lt;br /&gt;
	value = value:gsub(&amp;#039;&amp;gt;&amp;#039;, &amp;#039;&amp;amp;gt;&amp;#039;)&lt;br /&gt;
	return value&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function link(page, label)&lt;br /&gt;
	if not isNonEmpty(page) then return &amp;#039;&amp;#039; end&lt;br /&gt;
	label = label or page&lt;br /&gt;
	if page == label then&lt;br /&gt;
		return &amp;#039;[[&amp;#039; .. page .. &amp;#039;]]&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;#039;[[&amp;#039; .. page .. &amp;#039;|&amp;#039; .. label .. &amp;#039;]]&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function loadData(title)&lt;br /&gt;
	local ok, data = pcall(mw.loadData, title)&lt;br /&gt;
	if ok and type(data) == &amp;#039;table&amp;#039; then&lt;br /&gt;
		return data&lt;br /&gt;
	end&lt;br /&gt;
	return {}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function copyRecord(record)&lt;br /&gt;
	local out = {}&lt;br /&gt;
	for k, v in pairs(record) do&lt;br /&gt;
		out[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function miningProcedures()&lt;br /&gt;
	local out = {}&lt;br /&gt;
	for _, record in ipairs(loadData(&amp;#039;Module:Data/Procedures/Mining&amp;#039;)) do&lt;br /&gt;
		table.insert(out, copyRecord(record))&lt;br /&gt;
	end&lt;br /&gt;
	return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function engineerProcedures()&lt;br /&gt;
	local out = {}&lt;br /&gt;
	for _, name in ipairs(loadData(&amp;#039;Module:Data/Engineers/index&amp;#039;)) do&lt;br /&gt;
		local record = loadData(&amp;#039;Module:Data/Engineers/&amp;#039; .. name)&lt;br /&gt;
		local scope = record.engineer_scope or &amp;#039;ship&amp;#039;&lt;br /&gt;
		local topic = scope == &amp;#039;odyssey&amp;#039; and &amp;#039;Odyssey / On-foot&amp;#039; or &amp;#039;Engineering&amp;#039;&lt;br /&gt;
		table.insert(out, {&lt;br /&gt;
			key = &amp;#039;unlock-&amp;#039; .. name:gsub(&amp;#039;[^%w]+&amp;#039;, &amp;#039;-&amp;#039;):lower(),&lt;br /&gt;
			title = &amp;#039;Unlocking &amp;#039; .. name,&lt;br /&gt;
			page = &amp;#039;Unlocking &amp;#039; .. name,&lt;br /&gt;
			summary = &amp;#039;Gain access to &amp;#039; .. name .. &amp;#039; and their Engineering services.&amp;#039;,&lt;br /&gt;
			topic = topic,&lt;br /&gt;
			activity = scope == &amp;#039;odyssey&amp;#039; and &amp;#039;Odyssey Engineering&amp;#039; or &amp;#039;Engineering&amp;#039;,&lt;br /&gt;
			category = &amp;#039;Field Procedures&amp;#039;,&lt;br /&gt;
			related_reference_pages = { name, &amp;#039;Engineers&amp;#039;, &amp;#039;Engineering&amp;#039;, &amp;#039;Engineer Unlock Guide&amp;#039; },&lt;br /&gt;
			status = &amp;#039;Needs review&amp;#039;,&lt;br /&gt;
		})&lt;br /&gt;
	end&lt;br /&gt;
	return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function allProcedures()&lt;br /&gt;
	local out = {}&lt;br /&gt;
	for _, record in ipairs(miningProcedures()) do table.insert(out, record) end&lt;br /&gt;
	for _, record in ipairs(engineerProcedures()) do table.insert(out, record) end&lt;br /&gt;
	table.sort(out, function(a, b)&lt;br /&gt;
		local at = a.topic or &amp;#039;&amp;#039;&lt;br /&gt;
		local bt = b.topic or &amp;#039;&amp;#039;&lt;br /&gt;
		if at ~= bt then return at &amp;lt; bt end&lt;br /&gt;
		return (a.title or a.page or &amp;#039;&amp;#039;) &amp;lt; (b.title or b.page or &amp;#039;&amp;#039;)&lt;br /&gt;
	end)&lt;br /&gt;
	return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function topicRank(topic)&lt;br /&gt;
	for i, value in ipairs(TOPIC_ORDER) do&lt;br /&gt;
		if value == topic then return i end&lt;br /&gt;
	end&lt;br /&gt;
	return 999&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function recordMatches(record, args)&lt;br /&gt;
	if isNonEmpty(args.topic) and record.topic ~= args.topic then return false end&lt;br /&gt;
	if isNonEmpty(args.activity) and record.activity ~= args.activity then return false end&lt;br /&gt;
	if isNonEmpty(args.page) then&lt;br /&gt;
		local needle = args.page&lt;br /&gt;
		local refs = record.related_reference_pages or {}&lt;br /&gt;
		local procedures = record.related_procedures or {}&lt;br /&gt;
		local found = record.page == needle or record.title == needle&lt;br /&gt;
		for _, value in ipairs(refs) do if value == needle then found = true end end&lt;br /&gt;
		for _, value in ipairs(procedures) do if value == needle then found = true end end&lt;br /&gt;
		if not found then return false end&lt;br /&gt;
	end&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function groupedProcedures(args)&lt;br /&gt;
	local groups = {}&lt;br /&gt;
	for _, record in ipairs(allProcedures()) do&lt;br /&gt;
		if recordMatches(record, args or {}) then&lt;br /&gt;
			local topic = record.topic or &amp;#039;Other&amp;#039;&lt;br /&gt;
			groups[topic] = groups[topic] or {}&lt;br /&gt;
			table.insert(groups[topic], record)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local topics = {}&lt;br /&gt;
	for topic in pairs(groups) do table.insert(topics, topic) end&lt;br /&gt;
	table.sort(topics, function(a, b)&lt;br /&gt;
		local ar = topicRank(a)&lt;br /&gt;
		local br = topicRank(b)&lt;br /&gt;
		if ar ~= br then return ar &amp;lt; br end&lt;br /&gt;
		return a &amp;lt; b&lt;br /&gt;
	end)&lt;br /&gt;
	for _, topic in ipairs(topics) do&lt;br /&gt;
		table.sort(groups[topic], function(a, b)&lt;br /&gt;
			return (a.title or a.page or &amp;#039;&amp;#039;) &amp;lt; (b.title or b.page or &amp;#039;&amp;#039;)&lt;br /&gt;
		end)&lt;br /&gt;
	end&lt;br /&gt;
	return topics, groups&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderItem(record)&lt;br /&gt;
	local title = record.title or record.page&lt;br /&gt;
	local page = record.page or title&lt;br /&gt;
	local summary = record.summary or &amp;#039;&amp;#039;&lt;br /&gt;
	local status = record.status or &amp;#039;&amp;#039;&lt;br /&gt;
	local statusText = &amp;#039;&amp;#039;&lt;br /&gt;
	if status == &amp;#039;Draft&amp;#039; or status == &amp;#039;Planned&amp;#039; then&lt;br /&gt;
		statusText = &amp;#039; &amp;lt;span class=&amp;quot;edfm-procedure-status&amp;quot;&amp;gt;(&amp;#039; .. status .. &amp;#039;)&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;#039;* &amp;#039; .. link(page, title) .. statusText .. (isNonEmpty(summary) and &amp;#039; — &amp;#039; .. summary or &amp;#039;&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.directory(frame)&lt;br /&gt;
	local args = frame.args or {}&lt;br /&gt;
	local topics, groups = groupedProcedures(args)&lt;br /&gt;
	local out = {}&lt;br /&gt;
	for _, topic in ipairs(topics) do&lt;br /&gt;
		table.insert(out, &amp;#039;== &amp;#039; .. topic .. &amp;#039; ==&amp;#039;)&lt;br /&gt;
		if topic == &amp;#039;Mining&amp;#039; then&lt;br /&gt;
			table.insert(out, &amp;#039;Procedures for finding mining locations, extracting resources from asteroids, handling limpets and refinery bins, and selling mined commodities.&amp;#039;)&lt;br /&gt;
		elseif topic == &amp;#039;Engineering&amp;#039; then&lt;br /&gt;
			table.insert(out, &amp;#039;Procedures for gaining access to ship Engineers and completing Engineering-related unlock workflows.&amp;#039;)&lt;br /&gt;
		elseif topic == &amp;#039;Odyssey / On-foot&amp;#039; then&lt;br /&gt;
			table.insert(out, &amp;#039;Procedures for gaining access to Odyssey Engineers and completing on-foot Engineering-related workflows.&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
		table.insert(out, &amp;#039;&amp;#039;)&lt;br /&gt;
		for _, record in ipairs(groups[topic]) do&lt;br /&gt;
			table.insert(out, renderItem(record))&lt;br /&gt;
		end&lt;br /&gt;
		table.insert(out, &amp;#039;&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	if #out == 0 then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(out, &amp;#039;\n&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.list(frame)&lt;br /&gt;
	local args = frame.args or {}&lt;br /&gt;
	local topics, groups = groupedProcedures(args)&lt;br /&gt;
	local out = {}&lt;br /&gt;
	for _, topic in ipairs(topics) do&lt;br /&gt;
		for _, record in ipairs(groups[topic]) do&lt;br /&gt;
			table.insert(out, renderItem(record))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if #out == 0 then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(out, &amp;#039;\n&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.relatedForCurrentPage(frame)&lt;br /&gt;
	local title = mw.title.getCurrentTitle().text&lt;br /&gt;
	local args = frame.args or {}&lt;br /&gt;
	args.page = args.page or title&lt;br /&gt;
	return p.list({ args = args })&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.referenceLinks(frame)&lt;br /&gt;
	local args = frame.args or {}&lt;br /&gt;
	local wanted = args.procedure or args.page or mw.title.getCurrentTitle().text&lt;br /&gt;
	for _, record in ipairs(allProcedures()) do&lt;br /&gt;
		if record.page == wanted or record.title == wanted or record.key == wanted then&lt;br /&gt;
			local refs = record.related_reference_pages or {}&lt;br /&gt;
			local out = {}&lt;br /&gt;
			for _, page in ipairs(refs) do table.insert(out, link(page)) end&lt;br /&gt;
			return table.concat(out, &amp;#039; &amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Sythan</name></author>
	</entry>
</feed>